Ausnahmen in Java, Teil 1: Grundlagen der Ausnahmebehandlung

Java-Ausnahmen sind Bibliothekstypen und Sprachfunktionen, die zur Darstellung und Behandlung von Programmfehlern verwendet werden. Wenn Sie verstehen möchten, wie Fehler im Quellcode dargestellt werden, sind Sie hier genau richtig. Zusätzlich zu einer Übersicht über Java-Ausnahmen werde ich Ihnen die Java-Sprachfunktionen zum Auslösen von Objekten, zum Ausprobieren von möglicherweise fehlgeschlagenem Code, zum Abfangen von ausgelösten Objekten und zum Bereinigen Ihres Java-Codes nach dem Auslösen einer Ausnahme vorstellen.

In der ersten Hälfte dieses Tutorials lernen Sie grundlegende Sprachfunktionen und Bibliothekstypen kennen, die es seit Java 1.0 gibt. In der zweiten Hälfte werden Sie erweiterte Funktionen entdecken, die in neueren Java-Versionen eingeführt wurden.

Beachten Sie, dass Codebeispiele in diesem Lernprogramm mit JDK 12 kompatibel sind.

download Code abrufen Laden Sie den Quellcode herunter, zum Beispiel Anwendungen in diesem Tutorial. Erstellt von Jeff Friesen für JavaWorld.

Was sind Java-Ausnahmen?

Ein Fehler tritt auf, wenn das normale Verhalten eines Java-Programms durch unerwartetes Verhalten unterbrochen wird. Diese Abweichung ist als Ausnahme bekannt . Ein Programm versucht beispielsweise, eine Datei zu öffnen, um ihren Inhalt zu lesen, aber die Datei existiert nicht. Java klassifiziert Ausnahmen in einige Typen. Betrachten wir also jede einzelne.

Überprüfte Ausnahmen

Java klassifiziert Ausnahmen, die sich aus externen Faktoren ergeben (z. B. eine fehlende Datei), als geprüfte Ausnahmen . Der Java-Compiler überprüft, ob solche Ausnahmen entweder dort behandelt (korrigiert) werden, wo sie auftreten, oder dokumentiert sind, um an anderer Stelle behandelt zu werden.

Ausnahmebehandlungsroutinen

Ein Ausnahmebehandler ist eine Codesequenz, die eine Ausnahme behandelt. Es fragt den Kontext ab, dh es liest Werte, die aus Variablen gespeichert wurden, die zum Zeitpunkt der Ausnahme im Gültigkeitsbereich waren, und verwendet dann das Gelernte, um das Java-Programm auf einen Fluss normalen Verhaltens zurückzusetzen. Beispielsweise kann ein Ausnahmebehandler einen gespeicherten Dateinamen lesen und den Benutzer auffordern, die fehlende Datei zu ersetzen.

Laufzeitausnahmen (nicht markiert)

Angenommen, ein Programm versucht, eine Ganzzahl durch eine Ganzzahl 0 zu teilen. Diese Unmöglichkeit veranschaulicht eine andere Art von Ausnahme, nämlich eine Laufzeitausnahme . Im Gegensatz zu aktivierten Ausnahmen ergeben sich Laufzeitausnahmen normalerweise aus schlecht geschriebenem Quellcode und sollten daher vom Programmierer behoben werden. Da der Compiler nicht überprüft, ob Laufzeitausnahmen behandelt oder dokumentiert werden, um an anderer Stelle behandelt zu werden, können Sie sich eine Laufzeitausnahme als nicht aktivierte Ausnahme vorstellen .

Informationen zu Laufzeitausnahmen

Sie können ein Programm ändern, um eine Laufzeitausnahme zu behandeln, aber es ist besser, den Quellcode zu korrigieren. Laufzeitausnahmen ergeben sich häufig aus der Übergabe ungültiger Argumente an die Methoden einer Bibliothek. Der fehlerhafte Aufrufcode sollte behoben sein.

Fehler

Einige Ausnahmen sind sehr schwerwiegend, da sie die Fähigkeit eines Programms gefährden, die Ausführung fortzusetzen. Ein Programm versucht beispielsweise, Speicher von der JVM zuzuweisen, aber es ist nicht genügend freier Speicher vorhanden, um die Anforderung zu erfüllen. Eine andere schwerwiegende Situation tritt auf, wenn ein Programm versucht, eine Klassendatei über einen Class.forName()Methodenaufruf zu laden , die Klassendatei jedoch beschädigt ist. Diese Art von Ausnahme wird als Fehler bezeichnet . Sie sollten niemals versuchen, Fehler selbst zu behandeln, da die JVM möglicherweise nicht in der Lage ist, sie zu beheben.

Ausnahmen im Quellcode

Eine Ausnahme kann im Quellcode als Fehlercode oder als Objekt dargestellt werden . Ich werde beides vorstellen und Ihnen zeigen, warum Objekte überlegen sind.

Fehlercodes gegenüber Objekten

Programmiersprachen wie C verwenden ganzzahlige Fehlercodes , um Fehler und Fehlerursachen darzustellen - dh Ausnahmen. Hier einige Beispiele:

if (chdir("C:\\temp")) printf("Unable to change to temp directory: %d\n", errno); FILE *fp = fopen("C:\\temp\\foo"); if (fp == NULL) printf("Unable to open foo: %d\n", errno);

Die chdir()Funktion von C (Verzeichnis ändern) gibt eine Ganzzahl zurück: 0 bei Erfolg oder -1 bei Fehler. In ähnlicher Weise gibt die Funktion fopen()(Datei öffnen) von C bei Nichterfüllung einen Nicht-Null- Zeiger (Ganzzahladresse) an eine FILEStruktur oder bei einem Fehler einen Null-Zeiger (durch Konstante dargestellt NULL) zurück. In beiden Fällen müssen Sie den errnoganzzahligen Fehlercode der globalen Variablen lesen, um die Ausnahme zu identifizieren, die den Fehler verursacht hat.

Fehlercodes stellen einige Probleme dar:

  • Ganzzahlen sind bedeutungslos; Sie beschreiben nicht die Ausnahmen, die sie darstellen. Was bedeutet zum Beispiel 6?
  • Das Zuordnen von Kontext zu einem Fehlercode ist umständlich. Beispielsweise möchten Sie möglicherweise den Namen der Datei ausgeben, die nicht geöffnet werden konnte, aber wo werden Sie den Dateinamen speichern?
  • Ganzzahlen sind beliebig, was beim Lesen des Quellcodes zu Verwirrung führen kann. Zum Beispiel ist es klarer , anzugeben if (!chdir("C:\\temp"))( !bedeutet NICHT), anstatt if (chdir("C:\\temp"))auf Fehler zu testen. 0 wurde jedoch ausgewählt, um den Erfolg anzuzeigen, und if (chdir("C:\\temp"))muss daher angegeben werden, um auf Fehler zu testen.
  • Fehlercodes sind zu leicht zu ignorieren, was zu fehlerhaftem Code führen kann. Beispielsweise könnte der Programmierer chdir("C:\\temp");die if (fp == NULL)Prüfung angeben und ignorieren . Darüber hinaus muss der Programmierer nicht prüfen errno. Wenn nicht auf Fehler getestet wird, verhält sich das Programm unregelmäßig, wenn eine der Funktionen eine Fehleranzeige zurückgibt.

Um diese Probleme zu lösen, verfolgte Java einen neuen Ansatz zur Ausnahmebehandlung. In Java kombinieren wir Objekte, die Ausnahmen beschreiben, mit einem Mechanismus, der auf dem Werfen und Abfangen dieser Objekte basiert. Hier sind einige Vorteile der Verwendung von Objekten gegenüber Fehlercode, um Ausnahmen zu kennzeichnen:

  • Ein Objekt kann aus einer Klasse mit einem aussagekräftigen Namen erstellt werden. Zum Beispiel ist FileNotFoundException(im java.ioPaket) aussagekräftiger als 6.
  • Objekte können den Kontext in verschiedenen Feldern speichern. Sie können beispielsweise eine Nachricht, den Namen der Datei, die nicht geöffnet werden konnte, die letzte Position, an der ein Analysevorgang fehlgeschlagen ist, und / oder andere Elemente in den Feldern eines Objekts speichern.
  • Sie verwenden keine ifAnweisungen, um auf Fehler zu testen. Stattdessen werden Ausnahmeobjekte an einen Handler ausgegeben, der vom Programmcode getrennt ist. Infolgedessen ist der Quellcode leichter zu lesen und weniger fehlerhaft.

Throwable und seine Unterklassen

Java bietet eine Hierarchie von Klassen, die verschiedene Arten von Ausnahmen darstellen. Diese Klassen werden in dem java.langWurzelpaket der ThrowableKlasse, zusammen mit seinen Exception, RuntimeExceptionund ErrorUnterklassen.

Throwableist die ultimative Oberklasse, wenn es um Ausnahmen geht. Es können nur Objekte Throwableund deren Unterklassen geworfen (und anschließend abgefangen) werden. Solche Objekte werden als Wurfgegenstände bezeichnet .

Ein ThrowableObjekt ist einer Detailmeldung zugeordnet , die eine Ausnahme beschreibt. Es werden mehrere Konstruktoren bereitgestellt, einschließlich des unten beschriebenen Paares, um ein ThrowableObjekt mit oder ohne Detailmeldung zu erstellen :

  • Throwable () erstellt eine ThrowableMeldung ohne Detail. Dieser Konstruktor eignet sich für Situationen, in denen kein Kontext vorhanden ist. Sie möchten beispielsweise nur wissen, dass ein Stapel leer oder voll ist.
  • Throwable (String message) erstellt eine Throwablewith messageals Detailnachricht . Diese Nachricht kann an den Benutzer ausgegeben und / oder protokolliert werden.

Throwablebietet die String getMessage()Methode zum Zurückgeben der Detailnachricht. Es bietet auch zusätzliche nützliche Methoden, die ich später vorstellen werde.

Die Ausnahmeklasse

Throwablehat zwei direkte Unterklassen. Eine dieser Unterklassen Exceptionbeschreibt eine Ausnahme, die sich aus einem externen Faktor ergibt (z. B. der Versuch, aus einer nicht vorhandenen Datei zu lesen). Exceptiondeklariert dieselben Konstruktoren (mit identischen Parameterlisten) wie Throwableund jeder Konstruktor ruft sein ThrowableGegenstück auf. Exceptionerbt Throwabledie Methoden; Es werden keine neuen Methoden deklariert.

Java bietet viele Ausnahmeklassen, die direkt in Unterklassen unterteilt sind Exception. Hier sind drei Beispiele:

  • CloneNotSupportedException signalisiert einen Versuch, ein Objekt zu klonen, dessen Klasse die CloneableSchnittstelle nicht implementiert . Beide Typen sind im java.langPaket enthalten.
  • IOException signalisiert, dass ein E / A-Fehler aufgetreten ist. Dieser Typ befindet sich im java.ioPaket.
  • ParseException signalisiert, dass beim Parsen von Text ein Fehler aufgetreten ist. Dieser Typ ist im java.textPaket enthalten.

Beachten Sie, dass jeder ExceptionUnterklassenname mit dem Wort endet Exception. Diese Konvention macht es einfach, den Zweck der Klasse zu identifizieren.

Sie werden normalerweise eine Unterklasse Exception(oder eine ihrer Unterklassen) mit Ihren eigenen Ausnahmeklassen (deren Namen mit enden sollten Exception). Hier einige Beispiele für benutzerdefinierte Unterklassen:

public class StackFullException extends Exception { } public class EmptyDirectoryException extends Exception { private String directoryName; public EmptyDirectoryException(String message, String directoryName) { super(message); this.directoryName = directoryName; } public String getDirectoryName() { return directoryName; } }

Das erste Beispiel beschreibt eine Ausnahmeklasse, für die keine Detailmeldung erforderlich ist. Es ist der Standardaufruf des Noargument-Konstruktors Exception(), der aufruft Throwable().

Das zweite Beispiel beschreibt eine Ausnahmeklasse, deren Konstruktor eine Detailmeldung und den Namen des leeren Verzeichnisses erfordert. Der Konstruktor ruft auf Exception(String message), was aufruft Throwable(String message).

Objekte, die aus Exceptionoder einer seiner Unterklassen (mit Ausnahme RuntimeExceptionoder einer seiner Unterklassen) instanziiert wurden, sind geprüfte Ausnahmen.

Die RuntimeException-Klasse

Exceptionwird direkt von klassifiziert RuntimeException, was eine Ausnahme beschreibt, die höchstwahrscheinlich auf schlecht geschriebenen Code zurückzuführen ist. RuntimeExceptiondeklariert dieselben Konstruktoren (mit identischen Parameterlisten) wie Exceptionund jeder Konstruktor ruft sein ExceptionGegenstück auf. RuntimeExceptionerbt Throwabledie Methoden. Es werden keine neuen Methoden deklariert.

Java bietet viele Ausnahmeklassen, die direkt in Unterklassen unterteilt sind RuntimeException. Die folgenden Beispiele sind alle Mitglieder des java.langPakets:

  • ArithmeticException signalisiert eine unzulässige arithmetische Operation, z. B. den Versuch, eine Ganzzahl durch 0 zu teilen.
  • IllegalArgumentException signalisiert, dass ein unzulässiges oder unangemessenes Argument an eine Methode übergeben wurde.
  • NullPointerException signalisiert einen Versuch, eine Methode aufzurufen oder über die Nullreferenz auf ein Instanzfeld zuzugreifen.

Objekte, die aus RuntimeExceptionoder einer ihrer Unterklassen instanziiert wurden, sind nicht aktivierte Ausnahmen .

Die Fehlerklasse

Throwable's other direct subclass is Error, which describes a serious (even abnormal) problem that a reasonable application should not try to handle--such as running out of memory, overflowing the JVM's stack, or attempting to load a class that cannot be found. Like Exception, Error declares identical constructors to Throwable, inherits Throwable's methods, and doesn't declare any of its own methods.

You can identify Error subclasses from the convention that their class names end with Error. Examples include OutOfMemoryError, LinkageError, and StackOverflowError. All three types belong to the java.lang package.

Throwing exceptions

A C library function notifies calling code of an exception by setting the global errno variable to an error code and returning a failure code. In contrast, a Java method throws an object. Knowing how and when to throw exceptions is an essential aspect of effective Java programming. Throwing an exception involves two basic steps:

  1. Use the throw statement to throw an exception object.
  2. Use the throws clause to inform the compiler.

Later sections will focus on catching exceptions and cleaning up after them, but first let's learn more about throwables.

The throw statement

Java provides the throw statement to throw an object that describes an exception. Here's the syntax of the throw statement :

throw throwable;

The object identified by throwable is an instance of Throwable or any of its subclasses. However, you usually only throw objects instantiated from subclasses of Exception or RuntimeException. Here are a couple of examples:

throw new FileNotFoundException("unable to find file " + filename); throw new IllegalArgumentException("argument passed to count is less than zero");

The throwable is thrown from the current method to the JVM, which checks this method for a suitable handler. If not found, the JVM unwinds the method-call stack, looking for the closest calling method that can handle the exception described by the throwable. If it finds this method, it passes the throwable to the method's handler, whose code is executed to handle the exception. If no method is found to handle the exception, the JVM terminates with a suitable message.

The throws clause

You need to inform the compiler when you throw a checked exception out of a method. Do this by appending a throws clause to the method's header. This clause has the following syntax:

throws checkedExceptionClassName (, checkedExceptionClassName)*

A throws clause consists of keyword throws followed by a comma-separated list of the class names of checked exceptions thrown out of the method. Here is an example:

public static void main(String[] args) throws ClassNotFoundException { if (args.length != 1) { System.err.println("usage: java ... classfile"); return; } Class.forName(args[0]); }

This example attempts to load a classfile identified by a command-line argument. If Class.forName() cannot find the classfile, it throws a java.lang.ClassNotFoundException object, which is a checked exception.

Checked exception controversy

The throws clause and checked exceptions are controversial. Many developers hate being forced to specify throws or handle the checked exception(s). Learn more about this from my Are checked exceptions good or bad? blog post.