Einführung
Eine Ausnahme ist ein Fehlerereignis, das während der Ausführung eines Programms auftreten kann und seinen normalen Ablauf stört. Java bietet einen robusten und objektorientierten Ansatz zur Behandlung von Ausnahmeszenarien, bekannt als Java Exception Handling.
Ausnahmen in Java können aus verschiedenen Situationen entstehen, wie z. B. falschen Daten, die vom Benutzer eingegeben wurden, Hardwarefehlern, Netzwerkverbindungsfehlern oder einem nicht erreichbaren Datenbankserver. Der Code, der angibt, was in bestimmten Ausnahmesituationen zu tun ist, wird als Ausnahmebehandlung bezeichnet.
Ausnahmen werfen und abfangen
Java erstellt ein Ausnahmeobjekt, wenn während der Ausführung einer Anweisung ein Fehler auftritt. Das Ausnahmeobjekt enthält viele Debugging-Informationen wie Methodenhierarchie, Zeilennummer, an der die Ausnahme aufgetreten ist, und Typ der Ausnahme.
Wenn eine Ausnahme in einer Methode auftritt, wird der Vorgang des Erstellens des Ausnahmeobjekts und dessen Übergabe an die Laufzeitumgebung als „Ausnahme werfen“ bezeichnet. Der normale Ablauf des Programms wird unterbrochen, und die Java-Laufzeitumgebung (JRE) versucht, den Handler für die Ausnahme zu finden. Ein Ausnahme-Handler ist der Block von Code, der das Ausnahmeobjekt verarbeiten kann.
- Die Logik zur Suche nach dem Ausnahme-Handler beginnt mit der Suche in der Methode, in der der Fehler aufgetreten ist.
- Wenn kein geeigneter Handler gefunden wird, wird er zur aufrufenden Methode weitergeleitet.
- Und so weiter.
Wenn also der Aufrufstapel der Methode A->B->C
ist und eine Ausnahme in der Methode C
auftritt, wird die Suche nach dem geeigneten Handler von C->B->A
verschoben.
Wenn ein geeigneter Ausnahme-Handler gefunden wird, wird das Ausnahmeobjekt an den Handler übergeben, um es zu verarbeiten. Der Handler wird als „Ausnahme abfangen“ bezeichnet. Wenn kein geeigneter Ausnahme-Handler gefunden wird, wird das Programm beendet und Informationen über die Ausnahme in die Konsole gedruckt.
Das Java-Ausnahmebehandlungsframework wird nur zur Behandlung von Laufzeitfehlern verwendet. Kompilierungsfehler müssen vom Entwickler behoben werden, der den Code schreibt, sonst wird das Programm nicht ausgeführt.
Java-Schlüsselwörter zur Ausnahmebehandlung
Java bietet spezifische Schlüsselwörter für den Umgang mit Ausnahmen.
- throw – Wir wissen, dass bei einem Fehler ein Ausnahmeobjekt erstellt wird und die Java-Laufzeitumgebung mit der Verarbeitung beginnt, um sie zu behandeln. Manchmal möchten wir Ausnahmen explizit in unserem Code generieren. Zum Beispiel sollten wir in einem Benutzerauthentifizierungsprogramm Ausnahmen an Clients werfen, wenn das Passwort
null
ist. Das Schlüsselwortthrow
wird verwendet, um Ausnahmen an die Laufzeitumgebung zu werfen, damit sie behandelt werden können. - throws – Wenn wir in einer Methode eine Ausnahme werfen und sie nicht behandeln, müssen wir das Schlüsselwort
throws
in der Methodensignatur verwenden, um dem aufrufenden Programm die Ausnahmen mitzuteilen, die von der Methode geworfen werden können. Die aufrufende Methode kann diese Ausnahmen behandeln oder mit dem Schlüsselwortthrows
an ihre aufrufende Methode weitergeben. Wir können mehrere Ausnahmen in derthrows
-Klausel angeben, und es kann auch mit dermain()
-Methode verwendet werden. - try-catch – Wir verwenden den
try-catch
-Block zur Behandlung von Ausnahmen in unserem Code.try
markiert den Beginn des Blocks undcatch
befindet sich am Ende destry
-Blocks, um die Ausnahmen zu behandeln. Wir können mehrerecatch
-Blöcke mit einemtry
-Block haben. Dertry-catch
-Block kann auch verschachtelt sein. Dercatch
-Block erfordert einen Parameter, der vom TypException
sein sollte. - Endlich – der
finally
-Block ist optional und kann nur mit einemtry-catch
-Block verwendet werden. Da eine Ausnahme den Ausführungsvorgang stoppt, können einige Ressourcen geöffnet bleiben, die nicht geschlossen werden. Daher können wir denfinally
-Block verwenden. Derfinally
-Block wird immer ausgeführt, unabhängig davon, ob eine Ausnahme aufgetreten ist oder nicht.
Ein Beispiel für die Fehlerbehandlung
- Die Methode
testException()
wirft Ausnahmen mit demthrow
-Schlüsselwort. Die Methode signiert mit demthrows
-Schlüsselwort, um den Aufrufer über die Arten von Ausnahmen zu informieren, die auftreten können. - In der Methode
main()
behandele ich Ausnahmen mit demtry-catch
-Block in der Methodemain()
. Wenn ich sie nicht behandele, leite ich sie mit derthrows
-Klausel in der Methodemain()
weiter. - Die Methode
testException(-10)
wird aufgrund der Ausnahme niemals ausgeführt, und dann wird derfinally
-Block ausgeführt.
Die Methode printStackTrace()
ist eine der nützlichen Methoden in der Exception
-Klasse für Debugging-Zwecke.
Dieser Code gibt Folgendes aus:
Outputjava.io.FileNotFoundException: Negative Integer -5
at com.journaldev.exceptions.ExceptionHandling.testException(ExceptionHandling.java:24)
at com.journaldev.exceptions.ExceptionHandling.main(ExceptionHandling.java:10)
Releasing resources
Exception in thread "main" java.io.IOException: Only supported for index 0 to 10
at com.journaldev.exceptions.ExceptionHandling.testException(ExceptionHandling.java:27)
at com.journaldev.exceptions.ExceptionHandling.main(ExceptionHandling.java:19)
Einige wichtige Punkte, die zu beachten sind:
- Wir können keine
catch
– oderfinally
-Klausel ohne einetry
-Anweisung haben. - A
try
statement should have eithercatch
block orfinally
block, it can have both blocks. - Wir können keinen Code zwischen
try-catch-finally
-Blöcken schreiben. - Wir können mehrere
catch
-Blöcke mit einem einzigentry
-Statement haben. try-catch
-Blöcke können ähnlich wieif-else
-Anweisungen verschachtelt werden.- Wir können nur einen
finally
-Block mit einemtry-catch
-Statement haben.
Java-Ausnahme-Hierarchie
Wie bereits erwähnt, wird beim Auftreten einer Ausnahme ein Ausnahmeobjekt erstellt. Java-Ausnahmen sind hierarchisch strukturiert, und Vererbung wird verwendet, um verschiedene Arten von Ausnahmen zu kategorisieren. Throwable
ist die Elternklasse der Java-Ausnahme-Hierarchie und hat zwei Kindobjekte – Error
und Exception
. Exception
s sind weiter unterteilt in überprüfte Exception
s und Laufzeit-Exception
s.
- Fehler:
Error
s sind außergewöhnliche Szenarien, die außerhalb des Anwendungsbereichs liegen und nicht vorhersehbar und nicht wiederherstellbar sind. Beispielsweise Hardwarefehler, Absturz der Java Virtual Machine (JVM) oder Out-of-Memory-Fehler. Deshalb haben wir eine separate Hierarchie vonError
s und sollten versuchen, diese Situationen nicht zu behandeln. Einige der häufigstenError
s sindOutOfMemoryError
undStackOverflowError
. - Überprüfte Ausnahmen: Überprüfte
Exception
s sind außergewöhnliche Szenarien, mit denen wir in einem Programm rechnen können und versuchen, uns davon zu erholen. Zum BeispielFileNotFoundException
. Wir sollten diese Ausnahme abfangen und dem Benutzer eine nützliche Nachricht bereitstellen und sie ordnungsgemäß für Debugging-Zwecke protokollieren. DieException
ist die Oberklasse aller überprüftenException
s. Wenn wir eine überprüfteException
werfen, müssen wir sie im selben Methodenblock abfangen oder sie mit dem Schlüsselwortthrows
an den Aufrufer weiterleiten. - Laufzeit-Ausnahme: Laufzeit-
Exception
s werden durch schlechtes Programmieren verursacht. Zum Beispiel der Versuch, ein Element aus einem Array abzurufen. Wir sollten zuerst die Länge des Arrays überprüfen, bevor wir versuchen, das Element abzurufen, da sonst möglicherweise zur Laufzeit eineArrayIndexOutOfBoundException
ausgelöst wird.RuntimeException
ist die Oberklasse aller Laufzeit-Exception
s. Wenn wir in einer Methode eine Laufzeit-Exception
werfen, ist es nicht erforderlich, sie im Methodensignaturthrows
-Klausel anzugeben. Laufzeit-Ausnahmen können durch besseres Programmieren vermieden werden.
Einige nützliche Methoden von Exception-Klassen
Java Exception
und alle seine Unterklassen bieten keine spezifischen Methoden an, alle Methoden sind in der Basisklasse Throwable
definiert. Die Exception
-Klassen werden erstellt, um verschiedene Arten von Exception
-Szenarien anzugeben, damit wir die Ursache leicht identifizieren und die Exception
entsprechend ihres Typs behandeln können. Die Klasse Throwable
implementiert das Interface Serializable
für die Interoperabilität.
Einige der nützlichen Methoden der Klasse Throwable
sind:
- public String getMessage() – Diese Methode gibt die Nachricht
String
vonThrowable
zurück, und die Nachricht kann beim Erstellen der Ausnahme über ihren Konstruktor angegeben werden. - public String getLocalizedMessage() – Diese Methode wird bereitgestellt, damit Unterklassen sie überschreiben können, um eine sprachspezifische Nachricht für das aufrufende Programm bereitzustellen. Die
Throwable
-Klassenimplementierung dieser Methode verwendet die MethodegetMessage()
, um die Ausnahmemeldung zurückzugeben. - public synchronized Throwable getCause() – Diese Methode gibt die Ursache der Ausnahme zurück oder
null
, wenn die Ursache unbekannt ist. - public String toString() – Diese Methode gibt die Informationen über
Throwable
imString
-Format zurück, der zurückgegebeneString
enthält den Namen derThrowable
-Klasse und die lokalisierte Nachricht. - public void printStackTrace() – Diese Methode gibt die Stack-Trace-Informationen auf dem Standardfehlerstrom aus. Diese Methode ist überladen, und wir können
PrintStream
oderPrintWriter
als Argument übergeben, um die Stack-Trace-Informationen in die Datei oder den Strom zu schreiben.
Java 7 Automatisches Ressourcenmanagement und Verbesserungen im Catch-Block
Wenn Sie viele Ausnahmen in einem einzelnen try
-Block abfangen, werden Sie feststellen, dass der catch
-Blockcode größtenteils aus redundantem Code besteht, um den Fehler zu protokollieren. In Java 7 war eines der Features ein verbesserter catch
-Block, in dem wir mehrere Ausnahmen in einem einzelnen catch
-Block abfangen können. Hier ist ein Beispiel für den catch
-Block mit dieser Funktion:
Es gibt einige Einschränkungen, wie z.B. dass das Ausnahmeobjekt final ist und wir es nicht im catch
-Block ändern können. Lesen Sie die vollständige Analyse unter Java 7 Catch Block Improvements.
Die meiste Zeit verwenden wir den finally
-Block nur, um die Ressourcen zu schließen. Manchmal vergessen wir, sie zu schließen, und erhalten Laufzeit-Ausnahmen, wenn die Ressourcen erschöpft sind. Diese Ausnahmen sind schwer zu debuggen, und wir müssen möglicherweise jeden Ort überprüfen, an dem wir diese Ressource verwenden, um sicherzustellen, dass wir sie schließen. In Java 7 war eine der Verbesserungen try-with-resources
, bei der wir eine Ressource im try
-Statement erstellen können und sie innerhalb des try-catch
-Blocks verwenden. Wenn die Ausführung den try-catch
-Block verlässt, schließt die Laufzeitumgebung diese Ressourcen automatisch. Hier ist ein Beispiel für den try-catch
-Block mit dieser Verbesserung:
A Custom Exception Class Example
Java stellt viele Ausnahmeklassen für uns bereit, aber manchmal müssen wir unsere eigenen benutzerdefinierten Ausnahmeklassen erstellen. Zum Beispiel, um den Aufrufer über einen bestimmten Typ von Ausnahme mit der entsprechenden Meldung zu informieren. Wir können benutzerdefinierte Felder für die Verfolgung haben, wie beispielsweise Fehlercodes. Angenommen, wir schreiben eine Methode, die nur Textdateien verarbeiten soll, dann können wir dem Aufrufer den entsprechenden Fehlercode mitteilen, wenn eine andere Art von Datei als Eingabe gesendet wird.
Zuerst erstellen Sie MyException
:
Dann erstellen Sie ein CustomExceptionExample
:
Wir können eine separate Methode haben, um verschiedene Arten von Fehlercodes zu verarbeiten, die wir von verschiedenen Methoden erhalten. Einige von ihnen werden verbraucht, weil wir den Benutzer möglicherweise nicht darüber informieren möchten, oder einige davon werfen wir zurück, um den Benutzer über das Problem zu informieren.
Hier erweitere ich die Exception
, damit immer dann, wenn diese Ausnahme auftritt, sie in der Methode behandelt oder an das Aufrufprogramm zurückgegeben werden muss. Wenn wir RuntimeException
erweitern, ist es nicht erforderlich, dies im throws
-Klausel anzugeben.
Dies war eine Designentscheidung. Die Verwendung von überprüften Exception
s hat den Vorteil, Entwicklern dabei zu helfen, zu verstehen, welche Ausnahmen sie erwarten können, und angemessene Maßnahmen zu ergreifen, um sie zu behandeln.
Best Practices für die Behandlung von Ausnahmen in Java
- Verwenden Sie spezifische Ausnahmen – Basisklassen der Ausnahme-Hierarchie bieten keine nützlichen Informationen, deshalb hat Java so viele Ausnahme-Klassen, wie z.B.
IOException
mit weiteren Unterklassen wieFileNotFoundException
,EOFException
usw. Wir sollten immer spezifische Ausnahmeklassenthrow
undcatch
verwenden, damit der Aufrufer die Ursache der Ausnahme leicht erkennen und verarbeiten kann. Dies erleichtert das Debuggen und hilft Clientanwendungen, Ausnahmen angemessen zu behandeln. - Frühzeitig werfen oder Fail-Fast – Wir sollten versuchen, Ausnahmen so früh wie möglich zu werfen. Betrachten wir die obige
processFile()
-Methode, wenn wir dasnull
-Argument an diese Methode übergeben, erhalten wir die folgende Ausnahme:
OutputException in thread "main" java.lang.NullPointerException
at java.io.FileInputStream.<init>(FileInputStream.java:134)
at java.io.FileInputStream.<init>(FileInputStream.java:97)
at com.journaldev.exceptions.CustomExceptionExample.processFile(CustomExceptionExample.java:42)
at com.journaldev.exceptions.CustomExceptionExample.main(CustomExceptionExample.java:12)
Während des Debuggens müssen wir den Stapelrückverfolgungsbericht sorgfältig überprüfen, um den tatsächlichen Ort der Ausnahme zu identifizieren. Wenn wir unsere Implementationslogik ändern, um frühzeitig auf diese Ausnahmen zu überprüfen, wie unten dargestellt:
Dann wird der Ausnahme-Stapelrückverfolgungsbericht anzeigen, wo die Ausnahme mit einer klaren Meldung aufgetreten ist:
Outputcom.journaldev.exceptions.MyException: File name can't be null
at com.journaldev.exceptions.CustomExceptionExample.processFile(CustomExceptionExample.java:37)
at com.journaldev.exceptions.CustomExceptionExample.main(CustomExceptionExample.java:12)
- Spät Fangen – Da Java darauf besteht, die überprüfte Ausnahme entweder zu behandeln oder sie in der Methodensignatur zu deklarieren, neigen Entwickler manchmal dazu, die Ausnahme zu „fangen“ und den Fehler zu protokollieren. Diese Praxis ist jedoch schädlich, da das aufrufende Programm keine Benachrichtigung über die Ausnahme erhält. Wir sollten Ausnahmen nur „fangen“, wenn wir sie angemessen behandeln können. Zum Beispiel werfe ich in der obigen Methode Ausnahmen zurück an die aufrufende Methode, um sie dort zu behandeln. Die gleiche Methode könnte von anderen Anwendungen verwendet werden, die die Ausnahme auf eine andere Weise verarbeiten möchten. Bei der Implementierung einer Funktion sollten wir immer Ausnahmen an den Aufrufer zurückgeben und ihnen die Entscheidung überlassen, wie sie damit umgehen sollen.
- Ressourcen Schließen – Da Ausnahmen die Verarbeitung des Programms stoppen, sollten wir alle Ressourcen im finally-Block schließen oder die Java 7
try-with-resources
-Verbesserung verwenden, um Java die Schließung für Sie durchführen zu lassen. - Protokollierung von Ausnahmen – Wir sollten immer Ausnahmemeldungen protokollieren und beim
throw
en von Ausnahmen eine klare Meldung bereitstellen, damit der Aufrufer leicht erkennen kann, warum die Ausnahme aufgetreten ist. Wir sollten immer einen leerencatch
-Block vermeiden, der einfach die Ausnahme aufnimmt und keine sinnvollen Details zur Ausnahme für die Fehlersuche bereitstellt. - Einzelner catch-Block für mehrere Ausnahmen – In den meisten Fällen protokollieren wir Ausnahmedetails und geben eine Meldung an den Benutzer aus. In diesem Fall sollten wir die Java-7-Funktion verwenden, um mehrere Ausnahmen in einem einzigen
catch
-Block zu behandeln. Dieser Ansatz reduziert die Codegröße und sieht auch sauberer aus. - Verwendung von benutzerdefinierten Ausnahmen – Es ist immer besser, eine Ausnahmebehandlungsstrategie zur Entwurfszeit zu definieren und anstatt mehrere Ausnahmen zu
throw
en und zucatch
en, können wir eine benutzerdefinierte Ausnahme mit einem Fehlercode erstellen, und das Aufruferprogramm kann diese Fehlercodes behandeln. Es ist auch eine gute Idee, eine Hilfsmethode zu erstellen, um verschiedene Fehlercodes zu verarbeiten und zu verwenden. - Namenskonventionen und Verpackung – Wenn Sie Ihre benutzerdefinierte Ausnahme erstellen, stellen Sie sicher, dass sie mit
Exception
endet, damit aus dem Namen selbst klar wird, dass es sich um eine Ausnahme handelt. Stellen Sie auch sicher, dass sie wie im Java Development Kit (JDK) verpackt sind. Zum Beispiel istIOException
die Basisklasse für alle IO-Operationen. - Verwenden Sie Ausnahmen mit Bedacht – Ausnahmen sind kostspielig, und manchmal ist es überhaupt nicht erforderlich, Ausnahmen zu werfen, und wir können stattdessen eine boolesche Variable an das Aufrufprogramm zurückgeben, um anzuzeigen, ob eine Operation erfolgreich war oder nicht. Dies ist hilfreich, wenn die Operation optional ist und Sie nicht möchten, dass Ihr Programm stecken bleibt, weil es fehlschlägt. Zum Beispiel möchten wir beim Aktualisieren der Aktienkurse in der Datenbank aus einem Webdienst eines Drittanbieters das Werfen von Ausnahmen vermeiden, wenn die Verbindung fehlschlägt.
- Dokumentieren Sie die geworfenen Ausnahmen – Verwenden Sie Javadoc
@throws
, um die von der Methode geworfenen Ausnahmen klar zu spezifizieren. Dies ist sehr hilfreich, wenn Sie eine Schnittstelle für andere Anwendungen bereitstellen.
Fazit
In diesem Artikel haben Sie etwas über die Ausnahmebehandlung in Java gelernt. Sie haben über throw
und throws
gelernt. Sie haben auch über try
(und try-with-resources
), catch
und finally
-Blöcke gelernt.
Source:
https://www.digitalocean.com/community/tutorials/exception-handling-in-java