Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Industrial programming >> Java

Java Exceptions meistern: Unerwartete Fehler elegant behandeln

Was ist eine Exception?

Eine Exception (oder ein außergewöhnliches Ereignis) ist ein Problem, das während der Ausführung eines Programms auftritt. Wenn eine Exception auftritt, wird der normale Ablauf des Programms unterbrochen und das Programm/die Applikation beendet sich abnormal, was nicht empfohlen wird. Daher müssen diese Exceptions behandelt werden.

Eine Exception kann aus vielen verschiedenen Gründen auftreten. Folgende Szenarien führen zu Exceptions:

Einige dieser Exceptions werden durch Benutzerfehler verursacht, andere durch Programmierfehler und wieder andere durch physische Ressourcen, die auf irgendeine Weise versagt haben.

Basierend darauf gibt es drei Kategorien von Exceptions. Sie müssen sie verstehen, um zu wissen, wie Exception Handling in Java funktioniert.

Checked Exceptions (Überprüfte Ausnahmen)

Eine checked Exception ist eine Exception, die vom Compiler zur Kompilierzeit überprüft (benachrichtigt) wird. Diese werden auch als Compile-Time Exceptions bezeichnet. Diese Exceptions können nicht einfach ignoriert werden; der Programmierer sollte sich um diese Exceptions kümmern (sie behandeln).

Beispielsweise: Wenn Sie die FileReader-Klasse in Ihrem Programm verwenden, um Daten aus einer Datei zu lesen, und die in ihrem Konstruktor angegebene Datei nicht existiert, tritt eine FileNotFoundException auf, und der Compiler fordert den Programmierer auf, die Exception zu behandeln.

Beispiel

import java.io.File;
import java.io.FileReader;

public class FilenotFound_Demo {
    public static void main(String args[]) {
        File file = new File("E://file.txt");
        FileReader fr = new FileReader(file);
    }
}

Wenn Sie versuchen, das obige Programm zu kompilieren, erhalten Sie folgende Exceptions:

C:\>javac FilenotFound_Demo.java
FilenotFound_Demo.java:8: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
FileReader fr = new FileReader(file);
^
1 error

Hinweis – Da die Methoden read() und close() der FileReader-Klasse IOException werfen, können Sie beobachten, dass der Compiler auffordert, IOException zusammen mit FileNotFoundException zu behandeln.

Unchecked Exceptions (Nicht überprüfte Ausnahmen)

Eine unchecked Exception ist eine Exception, die zur Ausführungszeit auftritt. Diese werden auch als Runtime Exceptions bezeichnet. Dazu gehören Programmierfehler wie Logikfehler oder unsachgemäße Verwendung einer API. Runtime-Exceptions werden zur Kompilierzeit ignoriert.

Beispielsweise: Wenn Sie in Ihrem Programm ein Array der Größe 5 deklariert haben und versuchen, das 6. Element des Arrays aufzurufen, tritt eine ArrayIndexOutOfBoundsException auf.

Beispiel

public class Unchecked_Demo {
    public static void main(String args[]) {
        int num[] = {1, 2, 3, 4};
        System.out.println(num[5]);
    }
}

Wenn Sie das obige Programm kompilieren und ausführen, erhalten Sie folgende Exception:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at Exceptions.Unchecked_Demo.main(Unchecked_Demo.java:8)

Errors (Fehler)

Dies sind keine Exceptions, sondern Probleme, die außerhalb der Kontrolle des Benutzers oder Programmierers auftreten. Fehler werden in Ihrem Code normalerweise ignoriert, da Sie selten etwas gegen einen Fehler unternehmen können. Wenn beispielsweise ein Stack Overflow auftritt, entsteht ein Fehler. Sie werden auch zur Kompilierzeit ignoriert.

Exception-Hierarchie

Alle Exception-Klassen sind Subtypen der Klasse java.lang.Exception. Die Exception-Klasse ist eine Unterklasse der Throwable-Klasse. Außer der Exception-Klasse gibt es eine weitere Unterklasse namens Error, die von der Throwable-Klasse abgeleitet ist.

Errors sind abnormale Zustände, die bei schweren Fehlern auftreten; diese werden nicht von Java-Programmen behandelt. Errors werden generiert, um Fehler anzuzeigen, die von der Laufzeitumgebung generiert werden. Beispiel: JVM hat keinen Speicher mehr. Normalerweise können Programme sich nicht von Errors erholen.

Die Exception-Klasse hat zwei Hauptunterklassen: IOException-Klasse und RuntimeException-Klasse.

Wichtige Methoden der Throwable-Klasse

Folgende wichtige Methoden sind in der Throwable-Klasse verfügbar:

Nr.Methode & Beschreibung
1public String getMessage() – Gibt eine detaillierte Meldung über die aufgetretene Exception zurück. Diese Meldung wird im Throwable-Konstruktor initialisiert.
2public Throwable getCause() – Gibt die Ursache der Exception als Throwable-Objekt zurück.
3public String toString() – Gibt den Namen der Klasse zusammen mit dem Ergebnis von getMessage() zurück.
4public void printStackTrace() – Gibt das Ergebnis von toString() zusammen mit dem Stack Trace an System.err (den Fehlerausgabestream) aus.
5public StackTraceElement[] getStackTrace() – Gibt ein Array zurück, das jedes Element auf dem Stack Trace enthält. Das Element an Index 0 repräsentiert den oberen Rand des Call Stacks, und das letzte Element im Array repräsentiert die Methode am unteren Rand des Call Stacks.
6public Throwable fillInStackTrace() – Füllt den Stack Trace dieses Throwable-Objekts mit dem aktuellen Stack Trace, fügt ihn zu allen vorherigen Informationen im Stack Trace hinzu.

Exceptions abfangen (Catching Exceptions)

Eine Methode fängt eine Exception mithilfe einer Kombination der Schlüsselwörter try und catch ab. Ein try/catch-Block wird um den Code platziert, der eine Exception erzeugen könnte. Code innerhalb eines try/catch-Blocks wird als geschützter Code bezeichnet. Die Syntax für die Verwendung von try/catch sieht wie folgt aus:

Syntax

try {
    // Geschützter Code
} catch (ExceptionName e1) {
    // Catch-Block
}

Der Code, der anfällig für Exceptions ist, wird in den try-Block platziert. Wenn eine Exception auftritt, wird diese von dem damit verbundenen catch-Block behandelt. Jeder try-Block sollte sofort entweder von einem catch-Block oder einem finally-Block gefolgt werden.

Eine catch-Anweisung beinhaltet die Deklaration des Exception-Typs, den Sie abfangen möchten. Wenn eine Exception im geschützten Code auftritt, wird der catch-Block (oder die Blöcke), der/die auf den try-Block folgt, geprüft. Wenn der Typ der aufgetretenen Exception in einem catch-Block aufgeführt ist, wird die Exception an den catch-Block übergeben, ähnlich wie ein Argument an einen Methodenparameter übergeben wird.

Beispiel

Das folgende Array ist mit 2 Elementen deklariert. Dann versucht der Code, auf das 3. Element des Arrays zuzugreifen, was eine Exception auslöst.

// Dateiname: ExcepTest.java
import java.io.*;

public class ExcepTest {
    public static void main(String args[]) {
        try {
            int a[] = new int[2];
            System.out.println("Access element three :" + a[3]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Exception thrown :" + e);
        }
        System.out.println("Out of the block");
    }
}

Ausgabe:

Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block

Mehrere Catch-Blöcke

Ein try-Block kann von mehreren catch-Blöcken gefolgt werden. Die Syntax für mehrere catch-Blöcke sieht wie folgt aus:

Syntax

try {
    // Geschützter Code
} catch (ExceptionType1 e1) {
    // Catch-Block
} catch (ExceptionType2 e2) {
    // Catch-Block
} catch (ExceptionType3 e3) {
    // Catch-Block
}

Die obigen Aussagen demonstrieren drei catch-Blöcke, aber Sie können beliebig viele nach einem einzelnen try haben. Wenn eine Exception im geschützten Code auftritt, wird die Exception an den ersten catch-Block in der Liste geworfen. Wenn der Datentyp der geworfenen Exception mit ExceptionType1 übereinstimmt, wird er dort abgefangen. Wenn nicht, geht die Exception an den zweiten catch-Block weiter. Dies setzt sich fort, bis die Exception entweder abgefangen wird oder durch alle catches durchfällt, in welchem Fall die aktuelle Methode die Ausführung stoppt und die Exception an die vorherige Methode auf dem Call Stack weitergeworfen wird.

Beispiel

try {
    file = new FileInputStream(fileName);
    x = (byte) file.read();
} catch (IOException i) {
    i.printStackTrace();
    return -1;
} catch (FileNotFoundException f) // Nicht gültig! {
    f.printStackTrace();
    return -1;
}

Mehrere Exception-Typen in einem Catch-Block abfangen (ab Java 7)

Seit Java 7 können Sie mehr als eine Exception mit einem einzelnen catch-Block behandeln, was den Code vereinfacht. So geht es:

catch (IOException | FileNotFoundException ex) {
    logger.log(ex);
    throw ex;
}

Die Schlüsselwörter throws / throw

Wenn eine Methode eine checked Exception nicht behandelt, muss die Methode sie mit dem Schlüsselwort throws deklarieren. Das throws-Schlüsselwort erscheint am Ende der Methodensignatur.

Sie können eine Exception werfen, entweder eine neu instanziierte oder eine, die Sie gerade abgefangen haben, indem Sie das Schlüsselwort throw verwenden.

Versuchen Sie, den Unterschied zwischen throws und throw zu verstehen: throws wird verwendet, um die Behandlung einer checked Exception hinauszuzögern, und throw wird verwendet, um eine Exception explizit auszulösen.

Beispiel: throws

import java.io.*;

public class ClassName {
    public void deposit(double amount) throws RemoteException {
        // Methodenimplementierung
        throw new RemoteException();
    }
    // Rest der Klassendefinition
}

Eine Methode kann deklarieren, dass sie mehr als eine Exception wirft. In diesem Fall werden die Exceptions in einer durch Kommas getrennten Liste deklariert. Beispielsweise deklariert die folgende Methode, dass sie eine RemoteException und eine InsufficientFundsException wirft:

import java.io.*;

public class ClassName {
    public void withdraw(double amount) throws RemoteException, InsufficientFundsException {
        // Methodenimplementierung
    }
    // Rest der Klassendefinition
}

Der finally-Block

Der finally-Block folgt auf einen try-Block oder einen catch-Block. Ein finally-Block wird immer ausgeführt, unabhängig davon, ob eine Exception aufgetreten ist oder nicht.

Die Verwendung eines finally-Blocks ermöglicht es Ihnen, Bereinigungsanweisungen auszuführen, die Sie ausführen möchten, egal was im geschützten Code passiert.

Ein finally-Block erscheint am Ende der catch-Blöcke und hat folgende Syntax:

Syntax

try {
    // Geschützter Code
} catch (ExceptionType1 e1) {
    // Catch-Block
} catch (ExceptionType2 e2) {
    // Catch-Block
} catch (ExceptionType3 e3) {
    // Catch-Block
} finally {
    // Der finally-Block wird immer ausgeführt.
}

Beispiel

public class ExcepTest {
    public static void main(String args[]) {
        int a[] = new int[2];
        try {
            System.out.println("Access element three :" + a[3]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Exception thrown :" + e);
        } finally {
            a[0] = 6;
            System.out.println("First element value: " + a[0]);
            System.out.println("The finally statement is executed");
        }
    }
}

Ausgabe:

Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed

Beachten Sie Folgendes:

try-with-resources (automatische Ressourcenverwaltung)

Generell müssen wir, wenn wir Ressourcen wie Streams, Verbindungen usw. verwenden, diese explizit mit einem finally-Block schließen. Im folgenden Programm lesen wir Daten aus einer Datei mit FileReader und schließen sie mit einem finally-Block.

Beispiel: Klassischer Ansatz mit finally

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class ReadData_Demo {
    public static void main(String args[]) {
        FileReader fr = null;
        try {
            File file = new File("file.txt");
            fr = new FileReader(file);
            char[] a = new char[50];
            fr.read(a); // Inhalt in Array lesen
            for (char c : a)
                System.out.print(c); // Zeichen einzeln ausgeben
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fr.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

try-with-resources, auch als automatic resource management bezeichnet, ist ein neuer Exception-Handling-Mechanismus, der in Java 7 eingeführt wurde und die im try-catch-Block verwendeten Ressourcen automatisch schließt.

Um diese Anweisung zu verwenden, müssen Sie einfach die erforderlichen Ressourcen in Klammern deklarieren, und die erstellte Ressource wird am Ende des Blocks automatisch geschlossen. Folgende Syntax gilt für try-with-resources:

Syntax

try (FileReader fr = new FileReader("file path")) {
    // Ressource verwenden
} catch () {
    // catch-Body
}

Beispiel: try-with-resources

import java.io.FileReader;
import java.io.IOException;

public class Try_withDemo {
    public static void main(String args[]) {
        try (FileReader fr = new FileReader("E://file.txt")) {
            char[] a = new char[50];
            fr.read(a); // Inhalt in Array lesen
            for (char c : a)
                System.out.print(c); // Zeichen einzeln ausgeben
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Wichtige Punkte bei der Arbeit mit try-with-resources:

Benutzerdefinierte Exceptions (User-defined Exceptions)

Sie können in Java Ihre eigenen Exceptions erstellen. Beachten Sie folgende Punkte beim Schreiben eigener Exception-Klassen:

Wir können unsere eigene Exception-Klasse wie folgt definieren:

class MyException extends Exception {
}

Sie müssen lediglich die vordefinierte Exception-Klasse erweitern, um Ihre eigene Exception zu erstellen. Diese gelten als checked Exceptions. Die folgende InsufficientFundsException-Klasse ist eine benutzerdefinierte Exception, die die Exception-Klasse erweitert und damit zu einer checked Exception wird. Eine Exception-Klasse ist wie jede andere Klasse und enthält nützliche Felder und Methoden.

Beispiel: InsufficientFundsException

// Dateiname: InsufficientFundsException.java
import java.io.*;

public class InsufficientFundsException extends Exception {
    private double amount;

    public InsufficientFundsException(double amount) {
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }
}

Um die Verwendung unserer benutzerdefinierten Exception zu demonstrieren, enthält die folgende CheckingAccount-Klasse eine withdraw()-Methode, die eine InsufficientFundsException wirft.

// Dateiname: CheckingAccount.java
import java.io.*;

public class CheckingAccount {
    private double balance;
    private int number;

    public CheckingAccount(int number) {
        this.number = number;
    }

    public void deposit(double amount) {
        balance += amount;
    }

    public void withdraw(double amount) throws InsufficientFundsException {
        if (amount <= balance) {
            balance -= amount;
        } else {
            double needs = amount - balance;
            throw new InsufficientFundsException(needs);
        }
    }

    public double getBalance() {
        return balance;
    }

    public int getNumber() {
        return number;
    }
}

Das folgende BankDemo-Programm demonstriert das Aufrufen der deposit()- und withdraw()-Methoden von CheckingAccount.

// Dateiname: BankDemo.java
public class BankDemo {
    public static void main(String[] args) {
        CheckingAccount c = new CheckingAccount(101);
        System.out.println("Depositing $500...");
        c.deposit(500.00);
        try {
            System.out.println("\nWithdrawing $100...");
            c.withdraw(100.00);
            System.out.println("\nWithdrawing $600...");
            c.withdraw(600.00);
        } catch (InsufficientFundsException e) {
            System.out.println("Sorry, but you are short $" + e.getAmount());
            e.printStackTrace();
        }
    }
}

Kompilieren Sie alle drei Dateien und führen Sie BankDemo aus. Dies erzeugt folgendes Ergebnis:

Ausgabe

Depositing $500...
Withdrawing $100...
Withdrawing $600...
Sorry, but you are short $200.0
InsufficientFundsException
    at CheckingAccount.withdraw(CheckingAccount.java:25)
    at BankDemo.main(BankDemo.java:13)

Häufige Exceptions

In Java ist es möglich, zwei Kategorien von Exceptions und Errors zu definieren:

Java

  1. Java Operatoren meistern: Typen, Syntax und praktische Beispiele
  2. Java Kommentare: Typen, Verwendung und Best Practices
  3. Java Enhanced For-Schleife meistern: Syntax, Beispiele und Best Practices
  4. Java Strings meistern: Erstellung, Methoden und Best Practices
  5. Java Exception Handling meistern: Try, Catch, Finally, Throw & Throws erklärt
  6. Java throw vs. throws: Exception Handling mit praktischen Beispielen meistern
  7. Multi-Exception Handling in Java meistern
  8. Java Try-With-Resources meistern: Automatisches Ressourcenmanagement erklärt
  9. Java Annotations erklärt: Typen, Platzierung und praktische Beispiele
  10. Java Assertions: Praktischer Leitfaden zur Fehlererkennung und Code-Reliabilität