Folien - Fakultät Informatik Uni Stuttgart

Werbung
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Java: Kapitel 4
Fortgeschrittene Konzepte
Programmentwicklung WS 2008/2009
Holger Röder
[email protected]
© Holger Röder
„
„
„
„
„
„
„
„
Autoboxing
Erweiterte for-Schleife
Aufzählungen
Ausnahmebehandlung
Generische Klassen
Reflection
Annotationen
Garbage Collection
Programmentwicklung
Winter 2008/2009
Überblick über Kapitel 4
se
2
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Autoboxing
„ Zu jedem primitiven Datentyp existiert in Java eine Wrapper-Klasse
(int: Integer, char: Character, byte: Byte etc.), die den
jeweiligen Datentyp in einem Objekt „aufnehmen“.
„ Autoboxing: primitive Datentypen und Wrapper-Objekte werden bei
Bedarf implizit ineinander umgewandelt:
Integer i = 3; // Boxing, int -> Integer
int j = i; // Unboxing, Integer -> int
„ Damit sind – in Verbindung mit variablen Parameterlisten – Methoden
möglich, die beliebige Parameter beliebigen Typs akzeptieren:
public void print(Object... o) {
for (int i = 0; i < o.length; i++)
System.out.println("Parameter: "+ o[i]);
}
...
print("Hallo",3, 17.2, new Integer(4), 'x', false);
3
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Erweiterte for-Schleife
„ Seit Java 5 existiert eine erweiterte for-Schleife mit folgender Syntax:
for (formalerParameter: ausdruck) anweisung;
„ Dabei gilt:
† formalerParameter ist eine Parameterdeklaration,
z. B. String s
† ausdruck implementiert die Schnittstelle java.lang.Iterable
(wie z. B. die Listenklassen aus der Standardbibliothek)
oder ist ein []-Array
String[] texte = new String[3];
texte[0] = "Hallo ";
texte[1] = "Welt";
texte[2] = "!";
/* "Neue" Variante (mit Array): */
for(String s: texte) {
System.out.print(s);
}
4
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Aufzählungen
„ Mit dem Schlüsselwort enum können Aufzählungen
definiert werden:
public enum Farbe { ROT, GELB, GRUEN }
„ Aufzählungen sind Klassen, jedes Element einer Aufzählung ist ein
Objekt.
Farbe f1 = Farbe.GELB;
„ Standard-Methoden erlauben das Arbeiten mit Aufzählungsklassen und
Aufzählungsobjekten.
int i = f1.ordinal(); // liefert 1 (ROT=0, BLAU=2)
String s = f1.toString(); // liefert "GELB"
boolean b = f1.equals(Farbe.GELB); // liefert true
Farbe f2 = Farbe.valueOf("ROT"); // ok
Farbe f3 = Farbe.valueOf("BLAU"); // Exception!
5
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Aufzählungen in switch-Anweisungen
„ Aufzählungen können in switch-Anweisungen unterschieden werden:
Farbe f1 = Farbe.GELB;
...
switch (f1) {
case GELB:
System.out.println("Farbe ist GELB.");
break;
case GRUEN:
System.out.println("Farbe ist GRÜN");
break;
}
6
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Ausnahmebehandlung: Exceptions
„ In Java werden Ausnahmen (Exceptions) zur Fehlerbehandlung
verwendet. Viele Methoden der Java-Standardbibliothek „werfen“ im
Fehlerfall eine Exception.
„ Grundsätzlich gilt: Tritt eine Exception auf, muss sie entweder lokal
behandelt oder weitergegeben werden.
† Sonderfall: Ausnahmen vom Typ RuntimeException
(z. B. NullPointerException) können, müssen aber nicht
behandelt oder weitergegeben werden.
„ Für die lokale Ausnahmenbehandlung können Exceptions in einem
(try-)catch-Block „gefangen“ werden:
try {
double d = Double.parseDouble("Hallo");
System.out.println("Alles ok!"); // nie erreicht!
} catch (NumberFormatException e) {
System.out.println("Ungültiges Zahlenformat");
}
7
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Behandlung von Exceptions
„ Verschiedene Exceptions können über nacheinander folgende catchBlöcke gefangen werden. Der Typ der geworfenen Exception wird mit
dem Typ der zu fangenden Exception verglichen. Bei Übereinstimmung
wird zum jeweiligen catch-Block gesprungen.
„ Der (optionale) finally-Block wird immer aufgerufen (egal, ob eine
Exception bearbeitet wurde oder nicht).
try {
//... Hier können Exceptions geworfen werden ...
} catch(IOException e) {
System.out.println("IO-Fehler: " + e.getMessage());
} catch(Exception e) {
System.out.println("Fehler: " + e.getMessage());
} finally {
System.out.println("Programmende");
}
„ Anschließend fährt das Programm mit der ersten Anweisung nach dem
try-catch-(finally-)-Abschnitt fort.
8
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Weitergabe von Exceptions
„ Wenn die Ausnahmenbehandlung nicht lokal erfolgen soll, können
Exceptions an die aufrufende Methode weitergegeben werden.
„ Dazu muss der Methodenkopf mit einer throws-Klausel und der Liste
aller möglicherweise weitergegebenen Exceptions erweitert werden:
import java.io.*;
...
public String liesEin() throws IOException {
BufferedReader in = new BufferedReader(new
InputStreamReader(System.in));
String eingabe = in.readLine();
Wird eine Exception
return eingabe;
geworfen, wird diese an
die aufrufende Methode
}
weitergegeben.
„ Weitergegebene Exceptions müssen in der aufrufenden Methode
behandelt oder ebenfalls weitergegeben werden.
9
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Kombination von Behandlung und Weitergabe
„ Lokale Behandlung von Ausnahmen und Weitergabe können kombiniert
werden, wenn innerhalb eines catch-Blocks die gefangene Exception
mit throw weitergegeben wird.
public double liesZahl()
throws IOException, NumberFormatException {
...
try {
String eingabe = in.readLine();
} catch (IOException e) {
System.out.println("Fehler bei der Eingabe!");
throw e;
Zunächst lokale
}
Fehlerbehandlung…
...
}
… dann Weitergabe der Exception
10
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Eigene Exceptions
„ Auftretende Ausnahmen werden durch Objekte vom Typ Exception
(oder einer Unterklasse) repräsentiert.
„ Eigene Ausnahmen können als Unterklassen der Klasse Exception
erstellt werden.
MeineException.java
public class MeineException extends Exception {
public int fehlerCode;
...
}
„ Exceptions können mit der throw-Anweisung geworfen werden:
MeineException e = new MeineException();
e.fehlerCode = 127;
throw e;
11
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Generische Klassen
„ Generische Klassen (Generics) können verwendet werden, um eine
Implementierung, die prinzipiell alle Klassen unterstützt, typsicher auf
die Verwendung mit bestimmten Klassen zu beschränken.
„ Beispiel: Datenspeicher für 1 Element
IntegerSpeicher.java
public class IntegerSpeicher {
private Integer element;
public void put(Integer e) {
element = e;
}
public Integer get() {
return element;
}
}
StringSpeicher.java
public class StringSpeicher {
private String element;
public void put(String e) {
element = e;
}
public String get() {
return element;
}
}
Doppelter Code!
12
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Generische Klassen (2)
„ Implementierung als generische
Speicher.java
Klasse:
public class Speicher<T> {
private T element;
„ T dient als Platzhalter
public void put(T e) {
und wird bei der Deklaration und
element = e;
Initialisierung durch einen
}
konkreten Typ ersetzt.
public T get() {
return element;
}
}
„ Aufruf:
Speicher<Integer> i = new Speicher<Integer>();
i.put(new Integer(42));
Speicher<String> s = new Speicher<String>();
s.put("Ein String");
// s.put(new Integer(42)); // unzulässig, nur Strings!
13
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Generische Klassen – Polymorphie
„ Erinnerung: Polymorphie bei „normalen“ Klassen
class A { ... }
class B extends A { ... } // abgeleitete Klasse
...
B b_obj = new B();
A a_obj = b_obj; // zulässig, Polymorphie
„ Eine analoge Zuweisung mit generischen Klassen, die mit Ober- und
Unterklassen typisiert sind, ist jedoch nicht zulässig:
Speicher<B> b_speicher = new Speicher<B>;
Speicher<A> a_speicher = b_speicher; // nicht zulässig!
„ Wäre die letzte Anweisung zulässig, könnte beispielsweise folgende
Typinkompatibilität auftreten:
a_speicher.put(new A());
B obj1 = b_speicher.get();
/* B-Objekt erwartet, aber A-Objekt im Speicher! */
14
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Generische Klassen – Einschränkungen
„ Die Information über die Typisierung generischer Klassen ist zur
Laufzeit nicht bekannt (Type Erasure). Bei der Verwendung gibt es
deshalb Einschränkungen.
„ Der instanceof-Operator ist bei generischen Klassen nur zur Prüfung
der untypisierten generischen Klasse erlaubt.
if (b_speicher instanceof Speicher) { ... }
// erlaubt!
if (b_speicher instanceof Speicher<B>) { ... }
// nicht erlaubt!
„ Ebenso gilt:
† Parametrisierte Exception-Typen sind nicht erlaubt.
† Arrays parametrisierter Typen (Speicher<B>[] …) sind nicht
erlaubt.
15
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Generische Klassen – Wildcards
„ Die Wildcard ? kann bei der Typisierung generischer Klassen anstelle
eines konkreten Typs angegeben werden:
Speicher<B> b_speicher = new Speicher<B>;
Speicher<?> o_speicher = b_speicher;
„ Bei Verwendung der Wildcard ? sind Methoden, die einen generisch
typisierten Rückgabewert haben, nur noch mit Object typisiert:
Object obj2 = o_speicher.get(); // ok, liefert Object
B obj3
= o_speicher.get(); // nicht ok!
„ Der Aufruf von Methoden mit generisch typisierten Parametern ist
nicht mehr erlaubt:
o_speicher.put(new Object()); // nicht zulässig!
16
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Generische Klassen – Bounded Wildcards
„ Gebundene Wildcards (bounded wildcards) erlauben die Typisierung
mit der angegebenen Klasse oder einer Unterklasse:
Speicher<B> b_speicher
= new Speicher<B>;
Speicher<? extends A> a_speicher = b_speicher;
„ Der Aufruf von Methoden mit generisch typisierten Parametern (z. B.
Schreiboperationen) ist ebenfalls nicht erlaubt:
A obj4 = a_speicher.get(); // Lesen ok
a_speicher.put(new A()); // Schreiben nicht ok!
„ Mit dem Schlüsselwort super können gebundene Wildcards mit
unteren Grenzen definiert werden:
Speicher<? super B> speicher = ...
/* kann mit B oder Oberklasse von B typisiert werden */
17
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Generische Methoden
„ Auch einzelne Methoden können Typen generisch vorschreiben:
Auswahl.java
public class Auswahl {
public static <T> T zufall(T arg1, T arg2) {
// zufällig einen der beiden Parameter zurückgeben
return Math.random() > 0.5 ? arg1 : arg2;
}
}
...
String s = Auswahl.zufall("Hallo", "Welt"); // ok
Integer i = Auswahl.zufall(2, 3); // auch ok
Integer i = Auswahl.zufall("Hallo", "Welt"); // Fehler!
18
© Holger Röder
„ Das Reflection-API ermöglicht den Umgang mit Klassen und Objekten
zur Laufzeit. Dazu gehören etwa:
† das Laden und Instanziieren von Klassen,
† der Aufruf von Methoden und
† der Zugriff auf Attribute.
„ Reflection kommt häufig bei der Entwicklung generischer,
hochkonfigurierbarer oder durch Plug-Ins erweiterbarer Anwendungen
zum Einsatz (z. B. OR-Datenbank-Mapper etc.)
Programmentwicklung
Winter 2008/2009
Reflection
se
19
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Reflection – Class-Objekte
„ Die Klasse Class repräsentiert eine Java-Klasse zur Laufzeit und
ermöglicht den Zugriff auf Eigenschaften der Klasse.
„ Class-Objekte können auf verschiedene Weise erhalten werden:
† Die Methode getClass() der Klasse Object liefert ein
passendes Class-Objekt für das jeweilige Objekt.
String s = "Hallo";
Class c = s.getClass();
System.out.println(c.getName());
// Ausgabe: java.lang.String
† Jede Klasse besitzt ein statisches Class-Attribut namens class:
Class c = String.class;
† Die statische Methode Class.forName() liefert das ClassObjekt für die angegebene Klasse zurück:
Class c = Class.forName("java.lang.String");
„ Analog zu Class existieren die Klasse Method für Methodenzugriff
und Field für Attributzugriff.
20
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Reflection – Beispiel
public static void printMethods(String className)
throws ClassNotFoundException {
Sucht Klasse anhand
Class c = Class.forName(className);
des Klassennamens
for (Method m : c.getMethods()) {
Liefert alle
System.out.print(m.getReturnType().
Methoden
getSimpleName() + " ");
der Klasse
System.out.print(m.getName() + "(");
String delim = "";
for (Class param : m.getParameterTypes()) {
System.out.print(delim);
System.out.print(param.getSimpleName());
delim = ", ";
}
System.out.println(")");
}
}
21
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Reflection – Beispiel (Ausgabe)
„ printMethods("java.lang.String"); liefert
int hashCode()
int compareTo(String)
int compareTo(Object)
int indexOf(int, int)
int indexOf(int)
int indexOf(String)
int indexOf(String, int)
boolean equals(Object)
String toString()
char charAt(int)
int codePointAt(int)
int codePointBefore(int)
int codePointCount(int, int)
int compareToIgnoreCase(String)
String concat(String)
boolean contains(CharSequence)
boolean contentEquals(StringBuffer)
boolean contentEquals(CharSequence)
...
22
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Annotationen
„ Annotationen sind Metadaten, die im Quellcode dokumentiert werden.
„ Sie können zum „Kommentieren“ des Codes verwendet werden, sind
aber – anders als normale Kommentare – Teil des Syntaxbaums.
„ Es existiert eine Reihe vordefinierter Annotationen, z. B.
† @Deprecated – kennzeichnet Klasse/Methode als „veraltet“
† @Override – kennzeichnet überlagerte Methoden
„ Beispiel:
public class Person {
public double berechneGehalt() { ... }
}
public class Angestellter extends Person {
@Override
public double berechnGehalt() { ... }
}
Compiler meldet Fehler: die definierte Methode
überlagert keine Methode der Oberklasse!
23
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Eigene Annotationen
„ Die Definition eigener Annotationstypen erfolgt ähnlich wie die
Definition von Schnittstellen.
„ Damit Annotationen zur Laufzeit verfügbar sind, muss die
Annotationsklasse selbst entsprechend annotiert werden:
@Retention(RetentionPolicy.RUNTIME)
import java.lang.annotation.*;
// für Reflection (Zugriff zur Laufzeit):
@Retention(RetentionPolicy.RUNTIME)
public @interface Autor {
String vorname(); // Elemente des Annotationstyps
String nachname();
String firma() default "Firma XYZ"; // Standardwert
}
24
© Holger Röder
„ Nach der Definition können eigene Annotationen verwendet werden.
„ Die Elemente der Annotation werden über Schlüssel-Wert-Paare
zugewiesen.
public class Person {
@Autor (vorname = "Carl", nachname = "Coder")
public double berechneGehalt() { ... }
}
Programmentwicklung
Winter 2008/2009
Eigene Annotationen (2)
se
25
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Annotationen und Reflection
„ Annotationen können per Reflection abgefragt werden:
Person p = new Person();
for (Method m : p.getClass().getMethods()) {
if (m.isAnnotationPresent(Autor.class)) {
Autor autor = m.getAnnotation(Autor.class);
System.out.println("Methode '" + m.getName()
+ "' entwickelt von "
+ autor.vorname() + " "
+ autor.nachname());
}
}
„ Ausgabe:
Methode 'berechneGehalt' entwickelt von Carl Coder
26
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Garbage Collection in Java
„ Java besitzt ein automatisches Speichermanagement. Speicher muss
nicht explizit allokiert oder freigegeben werden.
„ Objekte, die nicht mehr referenziert werden, werden automatisch
(irgendwann) vom Garbage Collector gelöscht. Der Garbage Collector
läuft mit niedriger Priorität im Hintergrund.
Person carl = new Person(); // 1 Referenz auf das Objekt
Person p1 = carl // jetzt 2 Referenzen auf das Objekt
p1 = null;
// nur noch 1 Referenz auf das Objekt
carl = null;
// Objekt ohne Referenz
„ Vor dem Löschen eines Objekts wird jeweils die Destruktor-Methode
finalize() des Objekts aufgerufen.
„ Der Garbage Collector kann explizit gestartet werden: System.gc();
27
Herunterladen