04.11.2013 Aufrufe

Java / Kontrollfluss, Exceptions, Thread - Fachbereich 4: HTW Berlin

Java / Kontrollfluss, Exceptions, Thread - Fachbereich 4: HTW Berlin

Java / Kontrollfluss, Exceptions, Thread - Fachbereich 4: HTW Berlin

MEHR ANZEIGEN
WENIGER ANZEIGEN

Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.

YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.

Lecture Notes:<br />

<strong>Java</strong> / <strong>Kontrollfluss</strong>, <strong>Exceptions</strong>, <strong>Thread</strong><br />

(Version 1.0, vom 30.04.2010, Autor: Prof. Schwotzer)<br />

<strong>Java</strong> ist eine imperative Programmiersprache. Die einzelnen Statements eines Programmes<br />

beschreiben, was zu tun ist. Die Statements werden nacheinander abgearbeitet. Man nennt<br />

die Abfolge der Statements den <strong>Kontrollfluss</strong> (control flow). Der <strong>Kontrollfluss</strong> kann in<br />

Schleifen und Verzweigungen gebracht werden. Dazu dienen die bereits aus C bekannten<br />

Steuerelemente while, do-while, for und if-then-else. Diese Steuerelemente führen dazu, dass<br />

der Steuerfluss Zeilen überspringt bzw. wieder in bereits abgearbeitete Statements<br />

zurückspringt. Diese Elemente sind aus C hinlänglich bekannt und sollen hier nicht<br />

wiederholt werden.<br />

In <strong>Java</strong> spricht man auch davon, dass sich ein Asuführungsfaden (thread) durch das<br />

Programm zieht. Der erste Ausführungsfaden beginnt mit dem ersten Statement der<br />

gestarteten Main-Methode. Dieser <strong>Thread</strong> endet, wenn die Main-Methode beendet ist.<br />

Visualisieren Sie sich das einmal an einem beliebigem Programm.<br />

In <strong>Java</strong> kann man diesen <strong>Thread</strong> manipulieren, d.h. man kann ihn z.B. verzögern. Das<br />

geschieht durch folgendes Kommando:<br />

<strong>Thread</strong>.sleep(100);<br />

Das obige Statement sorgt dafür, dass der Ausführungsthread 100 Millisekunden pausiert und<br />

dann weiterläuft. Das Konzept des <strong>Thread</strong> mag auf Anhieb etwas eigenartig erscheinen.<br />

Versuchen Sie es zu verinnerlichen, auch wenn es etwas schwer vorstellbar wirken sollte.<br />

Konsequent ist es allemal:<br />

Der Ausführungsfaden, der <strong>Kontrollfluss</strong> ist ein Ding. Es ist eine Entität, die eine wesentliche<br />

Rolle in einem OO-System spielt. Konsequenterweise existiert ein Objekt, dass diese<br />

<strong>Kontrollfluss</strong> repräsentiert. Und dieses Objekt kann man manipulieren. Lesen Sie sich einmal<br />

weitere Methoden der Klasse <strong>Thread</strong> durch. Wie werden im Abschnitt „Multithreading“<br />

darauf zurückkommen.<br />

Ausnahmen<br />

Wenn Sie eine Methode aufrufen, können grob drei Dinge passieren:<br />

• Die Methode wird erfolgreich ausgeführt.<br />

• Bei der Ausführung der Methode entstand ein Fehler. Das kann verschiedene<br />

Ursachen haben<br />

◦ Sie haben sich beim Aufruf nicht an den Contract gehalten, d.h. Es wurden falsche<br />

Eingabeparameter übergeben,<br />

◦ Während der Ausführung zeigte sich, trotz korrekter Eingabewerte, dass die<br />

Methode nicht korrekt ausgeführt werden kann. Sie kann kein Ergebnis liefern.<br />

• Während der Ausführung entstand ein Fehler, der nicht im Programm, sondern zur<br />

Laufzeit des Programmes auftrat. Vielleicht ist die Datenbank gerade ausgefallen, auf


die Sie zugreifen. Das File, das just geöffnet wurde, lässt sich aus unklaren Gründen<br />

nicht mehr schreiben, vielleicht ist der USB-Stick entfernt worden, …<br />

In C werden diese Fälle lediglich durch den Rückgabeparameter entschieden. Ein guter<br />

Aufruf in C sieht so aus:<br />

int retval = f();<br />

if(retval == ERROR) {<br />

/* do something */<br />

} else {<br />

/* there is a result – do something useful */<br />

}<br />

Im Prinzip muss nach jedem Funktionsaufruf geprüft werden, ob die Funktion überhaupt<br />

erfolgreich ausgeführt werden konnte. Das ist a) nertötend und b) unhandlich, weshalb viele<br />

C-Programme darauf verzichten, was sie noch fehleranfälliger machen.<br />

In <strong>Java</strong> hat man die unterschiedlichen Aspekte eines Rückgabewertes eine Methode getrennt,<br />

indem das Konzept der Exception eingeführt wurde. Folgendes Codebeispiel soll die Nutzung<br />

einer Exception illustrieren:<br />

public void divide(int a, int b) throws Exception {<br />

if(b == 0) {<br />

Exception e = new Exception(“b darf nicht 0 sein“);<br />

throw e;<br />

}<br />

}<br />

return a / b;<br />

Exception ist eine Klasse. Von ihr können Instanzen erzeugt werden. Das erfolgt mittels<br />

new(). Ein Exception-Objekt repräsentiert einen konkreten Fehlerfall. In obigen Beispiel wird<br />

ein Fehlerfall erkannt, wenn der zweiten Parameter 0 ist. In dem Fall wird ein Objekt erzeugt,<br />

dass dieses Fehlerfall dokumentiert. Danach wird dieser Fehler mittels throw() geworfen. An<br />

dieser Stelle wird der <strong>Kontrollfluss</strong> unterbrochen und an der Stelle fortgesetzt, an der die<br />

Methode aufgerufen wurde.<br />

Beachten Sie, dass Methoden deklarieren müssen, wenn das Werfen einer Exception möglich<br />

ist. Das erfolgt mittels des Statement throws Exception.<br />

Ein aufrufender Prozess kann auf zwei Arten mit möglichen <strong>Exceptions</strong> umgehen: Er kann sie<br />

behandeln oder ebenfalls werfen. Beispiel:<br />

...<br />

int a = ?;<br />

int b = ?;<br />

int result;<br />

try {<br />

result = this.divide(a, b);<br />

}<br />

catch(Exception e) {<br />

System.err.println(“etwas wirklich dummes passierte hier: “ + e.getMessage());<br />

}<br />

finally {<br />

// was immer passierte, finally wird immer ausgeführt<br />

}


Wenn mögliche <strong>Exceptions</strong> bearbeitet werden sollen, so müssen sie in einem try-catch-Block<br />

stehen, siehe oben. Try bedeutet Versuchen. Es wird also versucht, Methoden auszuführen. In<br />

dem Moment, wenn eine Methode mit einer Exception abgebrochen wird, wird auch der<br />

<strong>Kontrollfluss</strong> innerhalb des try-Block abgebrochen.<br />

Die Metapher bei Exception ist diese: Ein Exception-Objekt repräsentiert ein Problem, einen<br />

Fehler, der zum Abbruch der Methode führte. Dieses Objekt wird aus der abgebrochenen<br />

Methode herausgeworfen (throw). Wird eine Exception-Objekt nicht gefangen, so bricht es<br />

auch die aufrufenden Methode ab und so weiter, bis es bei einer ersten main()-Methode<br />

landet.<br />

Exception-Objekte können aber gefangen werden (catch). Nun ist die Abbruchkaskade<br />

unterbrochen. Die Exception wird behandelt. Die Art der Behandlung ist frei. Nicht selten,<br />

wird das Problem lediglich dokumentiert, ein Default-Wert wird gesetzt und die Methode<br />

läuft weiter. Manchmal wird das Problem dokumentiert und die Exception wird weiter<br />

geworfen. Es steht Ihnen frei, mit <strong>Exceptions</strong> umzugehen.<br />

Es gibt einen dritten Block, der finally-Block. Er wird eher selten genutzt. Dieser Code wird in<br />

jedem Fall abgearbeitet, egal ob eine Exception geworfen wurde oder nicht.<br />

Ein Beispiel im Semiar wird das Vorgehen vertiefen. Lesen Sie auch in einem <strong>Java</strong>-Buch das<br />

Konzept der <strong>Exceptions</strong> nach.<br />

Klassen von <strong>Exceptions</strong><br />

Es gibt eine Klassenhierarchie von Ausnahmen. Die Superklasse ist Throwable (Werbare).<br />

Davon abgeleitet sind RuntimeException und Exception. Der Unterschied ist synaktischer<br />

und semantischer Natur. Runtime<strong>Exceptions</strong> sind Fehler, die zur Laufzeit entstehen können<br />

und auf deren Entstehung des Programm keinen Einfluss hat: Ausfall einer Datenbank,<br />

Zusammenbruch des Netzwerkes, Fehler in externen Datenträgern sind Beispiele von<br />

Laufzeitfehlern. Laufzeitfehler können jederzeit entstehen und jederzeit abgefangen werden.<br />

Runtime<strong>Exceptions</strong> müssen aber nicht gefangen werden. Exception ist die Basisklasse aller<br />

Ausnahmen, die im Kontext der Anwendung entstehen können. Sie zeigen Fehler an, die sich<br />

aus der eigenen Anwendung, dem eigenen Programm, z.B. durch fehlerhalftes Verwenden von<br />

Parameter etc. ergeben. <strong>Exceptions</strong> werden in der Methode deklariert und müssen gefangen<br />

oder geworfen werden.<br />

Einige Regeln<br />

• Ein klassischer Anfängerfehler besteht in der Annahme, dass das Werfen von<br />

<strong>Exceptions</strong> eine Art Unfähigkeit andeutet. Hier stimmt aber das Gegenteil. Profis<br />

dokumentieren Fehler, Anfänger tendieren dazu, dass Fehler als etwas schlechtes zu<br />

erkennen und meinen dass deren Existenz irgendwie und möglichst schnell behoben<br />

werden muss. Das ist in realen Systemen in der Regel nicht möglich. Wie wollen Sie<br />

bspw. in einer Methode arbeiten, die Zahlen addiert, wenn plötzlich eine Datenbank<br />

ausfällt. Sehr häufig gibt es keine eindeutig und klaren Möglichkeiten, mit einem<br />

Problem umzugehen. Das Werfen einer Exception ist daher der empfohlene Weg,<br />

Probleme zu dokumentieren und zu publizieren. Arbeiten Sie professionell und<br />

werfen Sie <strong>Exceptions</strong>!<br />

• Es ist guter Stil, wenn die try-Blöcke klein sind. Der Code mag eigenartig aussehen,


wenn nur wenige Methoden geklammert sind und ständig <strong>Exceptions</strong> abgefangen<br />

werden. Ein Anfängerfehler besteht darin, lange try-Blöcke zu schreiben, die aber<br />

nicht zusammen gehören. Statement gehören dann in einen try-Block, wenn nach<br />

dem Abbruch der Abarbeitung keine Statement, aber auch wirklich kein weiteres<br />

Statement sinnvoll ausgeführt werden kann. Dann kann der gesamte Block<br />

unterbrochen werden und die Ausnahmebehandlung kann starten.<br />

Wenn aber die Abarbeitung mittels des Setzens eine Defaults oder ähnlichen<br />

weitergehen kann, so ist der try-Block zu trennen. Das werden wir üben.<br />

• Definieren Sie eigene Exception-Klassen. Wenn Sie eine eigene Anwendung haben, so<br />

definieren Sie sich eine eigenen Exception-Klasse<br />

class MyException extends Exception {}<br />

Auf diese Weise können Sie überall erkennen, ob die Exception aus Ihrem System<br />

kommt oder von anderen Stellen. Wenn Sie unterschiedliche Klassen von Ausnahmen<br />

haben, so definieren Sie mehrere Klassen, z.B. WrongParameterMyException,<br />

NoUserReplyMyException, die alle von Ihrer Basisklasse ableiten. Dadurch wird<br />

solcher Code möglich:<br />

...<br />

try {...}<br />

catch(WrongParameterMyException wpme) { ... }<br />

catch(NoUserReplyMyException nurme) { ... }<br />

catch(MyException me) { ... }<br />

catch(Exception e) { ... }<br />

Durch so ein Vorgehen, können Sie zunächst sehr spezielle und dann immer<br />

konkretere Ausnahmen auffangen. Vieles catch-Blöcke zeugen in der Regel von einem<br />

professionellen Vorgehen. Hier wurden mögliche Fehler und deren Behandlung<br />

ausführlich beachtet.<br />

• Fangen Sie nur Runtime<strong>Exceptions</strong>, wenn Sie genau wissen, was Sie dagegen tun<br />

können. Es ist manchmal deutlich besser, wenn ein Programm kontrolliert terminiert,<br />

als wenn es irgendwie weiterläuft. Beispiel: Wenn das Netzwerk zusammenbricht, so<br />

können Sie in Ihrem <strong>Java</strong>-Programm durchaus <strong>Exceptions</strong> dazu fangen und das<br />

Programm am laufen halten. Es wird nur keinen sinnvollen Beitrag mehr liefern.<br />

Beenden Sie es besser. Dokumentieren Sie ggf. den Fehler, aber machen Sie Schluss.

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!