Ausnahmebehandlung in Java

1. Überblick

In diesem Lernprogramm werden die Grundlagen der Ausnahmebehandlung in Java sowie einige der dazugehörigen Argumente erläutert.

2. Erste Prinzipien

2.1. Was ist es?

Um Ausnahmen und die Behandlung von Ausnahmen besser zu verstehen, nehmen wir einen echten Vergleich vor.

Stellen Sie sich vor, wir bestellen ein Produkt online, aber während des Transports ist die Lieferung fehlerhaft. Eine gute Firma kann dieses Problem lösen und unser Paket behutsam umleiten, damit es noch rechtzeitig ankommt.

In Java kann der Code bei der Ausführung unserer Anweisungen ebenfalls zu Fehlern führen. Eine gute Ausnahmebehandlung kann Fehler behandeln und das Programm ordnungsgemäß umleiten, um dem Benutzer noch eine positive Erfahrung zu geben. _. _

2.2. Warum es benutzen?

Normalerweise schreiben wir Code in einer idealisierten Umgebung: Das Dateisystem enthält immer unsere Dateien, das Netzwerk ist fehlerfrei und die JVM verfügt immer über ausreichend Speicher. Manchmal nennen wir das den "glücklichen Weg".

In der Produktion können Dateisysteme jedoch beschädigt werden, Netzwerke können ausfallen und JVMs verfügen nicht mehr über genügend Arbeitsspeicher. Das Wohlbefinden unseres Codes hängt davon ab, wie er mit „unglücklichen Pfaden“ umgeht.

Wir müssen mit diesen Bedingungen umgehen, da sie den Fluss der Anwendung negativ beeinflussen und Exceptions bilden:

public static List<Player> getPlayers() throws IOException {
    Path path = Paths.get("players.dat");
    List<String> players = Files.readAllLines(path);

    return players.stream()
      .map(Player::new)
      .collect(Collectors.toList());
}

Dieser Code entscheidet sich dafür, die IOException nicht zu behandeln, sondern stattdessen an den Aufrufstapel zu übergeben. In einer idealisierten Umgebung funktioniert der Code gut.

Aber was könnte in der Produktion passieren, wenn _players.dat _ fehlt?

Exception in thread "main" java.nio.file.NoSuchFileException: players.dat <-- players.dat file doesn't exist
    at sun.nio.fs.WindowsException.translateToIOException(Unknown Source)
    at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
   //... more stack trace
    at java.nio.file.Files.readAllLines(Unknown Source)
    at java.nio.file.Files.readAllLines(Unknown Source)
    at Exceptions.getPlayers(Exceptions.java:12) <-- Exception arises in getPlayers() method, on line 12
    at Exceptions.main(Exceptions.java:19) <-- getPlayers() is called by main(), on line 19
  • Ohne diese Ausnahme kann ein ansonsten fehlerfreies Programm nicht mehr laufen! ** Wir müssen sicherstellen, dass unser Code einen Plan hat, wenn etwas schief läuft.

Beachten Sie auch einen weiteren Vorteil für Ausnahmen, und zwar die Stack-Trace selbst. Aufgrund dieser Stack-Ablaufverfolgung können wir häufig fehlerhaften Code ermitteln, ohne einen Debugger anhängen zu müssen.

3. Ausnahmehierarchie

Letztendlich sind Ausnahmen nur Java-Objekte, wobei sich alle von Throwable erstrecken:

              ---> Throwable <---
              |    (checked)     |
              |                  |
              |                  |
      ---> Exception           Error
      |    (checked)        (unchecked)
      |
RuntimeException
  (unchecked)

Es gibt drei Hauptkategorien von außergewöhnlichen Bedingungen:

  • Überprüfte Ausnahmen

  • Nicht geprüfte Ausnahmen/Laufzeitausnahmen

  • Fehler

  • Laufzeit und ungeprüfte Ausnahmen beziehen sich auf dasselbe. Wir können sie oft austauschbar verwenden. **

3.1. Markierte Ausnahmen

Geprüfte Ausnahmen sind Ausnahmen, die der Java-Compiler verarbeiten muss. Wir müssen entweder die Ausnahme deklarativ in den Aufrufstapel werfen oder sie selbst behandeln. Mehr zu beiden in einem Moment.

Oracle’s Dokumentation weist uns an, geprüfte Ausnahmen zu verwenden, wenn wir vernünftigerweise erwarten können, dass der Aufrufer unserer Methode eine Wiederherstellung durchführen kann.

Einige Beispiele für geprüfte Ausnahmen sind IOException und ServletException.

3.2. Ungeprüfte Ausnahmen

Nicht geprüfte Ausnahmen sind Ausnahmen, für die der Java-Compiler nicht benötigt.

Einfach ausgedrückt: Wenn wir eine Ausnahme erstellen, die RuntimeException erweitert, wird diese Option deaktiviert. Andernfalls wird es geprüft.

Auch wenn dies praktisch klingt, sagt uns Oracle’s Dokumentation , dass es für beide Konzepte gute Gründe gibt, beispielsweise die Unterscheidung zwischen einem Situationsfehler (geprüft) ) und einen Verwendungsfehler (nicht markiert).

Beispiele für ungeprüfte Ausnahmen sind _NullPointerException, IllegalArgumentException, und SecurityException_ .

3.3. Fehler

Fehler stellen schwerwiegende und in der Regel nicht wiederherstellbare Bedingungen dar, wie z. B. eine Inkompatibilität der Bibliothek, eine unendliche Rekursion oder Speicherverluste.

Und obwohl sie RuntimeException nicht erweitern, werden sie auch nicht markiert.

In den meisten Fällen wäre es ungewöhnlich, Errors zu handhaben, instanziieren oder zu erweitern. Normalerweise wollen wir, dass sich diese nach oben ausbreiten.

Einige Beispiele für Fehler sind StackOverflowError und OutOfMemoryError .

4. Umgang mit Ausnahmen

In der Java-API gibt es viele Stellen, an denen Dinge schief gehen können. Einige dieser Orte sind mit Ausnahmen gekennzeichnet, entweder in der Signatur oder im Javadoc:

----/** **
 **  @exception FileNotFoundException ...
 ** /public Scanner(String fileName) throws FileNotFoundException {
  //...
}
----

Wie bereits erwähnt, müssen wir, wenn wir diese "riskanten" Methoden nennen, die markierten Ausnahmen behandeln, und wir können die ungeprüften behandeln. Java bietet uns dazu mehrere Möglichkeiten:

4.1. würfe

Die einfachste Möglichkeit, eine Ausnahme zu „handhaben“, besteht darin, sie erneut zu werfen:

public int getPlayerScore(String playerFile)
  throws FileNotFoundException {

    Scanner contents = new Scanner(new File(playerFile));
    return Integer.parseInt(contents.nextLine());
}

Da ____FileNotFoundException eine geprüfte Ausnahmebedingung ist, stellt dies die einfachste Möglichkeit dar, den Compiler zu befriedigen, aber bedeutet, dass jeder, der unsere Methode aufruft, dies ebenfalls zu behandeln hat.

parseInt kann eine NumberFormatException auslösen, da es jedoch nicht aktiviert ist, ist es nicht erforderlich, damit umzugehen.

4.2. try catch

Wenn wir versuchen, die Ausnahme selbst zu behandeln, können wir einen try-catch -Block verwenden. Wir können damit umgehen, indem wir unsere Ausnahme erneut werfen:

public int getPlayerScore(String playerFile) {
    try {
        Scanner contents = new Scanner(new File(playerFile));
        return Integer.parseInt(contents.nextLine());
    } catch (FileNotFoundException noFile) {
        throw new IllegalArgumentException("File not found");
    }
}

Oder indem Sie Wiederherstellungsschritte ausführen:

public int getPlayerScore(String playerFile) {
    try {
        Scanner contents = new Scanner(new File(playerFile));
        return Integer.parseInt(contents.nextLine());
    } catch ( FileNotFoundException noFile ) {
        logger.warn("File not found, resetting score.");
        return 0;
    }
}

4.3. endlich

Nun gibt es Zeiten, in denen Code ausgeführt werden muss, unabhängig davon, ob eine Ausnahmebedingung auftritt, und hier kommt das Schlüsselwort finally ins Spiel.

In unseren bisherigen Beispielen lauerte in den Schatten ein böser Fehler, der besagt, dass Java standardmäßig keine Dateihandles an das Betriebssystem zurückgibt.

Sicher, ob wir die Datei lesen können oder nicht, wir möchten sicherstellen, dass wir die entsprechende Bereinigung durchführen!

Versuchen wir es zuerst auf die „faule“ Art:

public int getPlayerScore(String playerFile)
  throws FileNotFoundException {
    Scanner contents = null;
    try {
        contents = new Scanner(new File(playerFile));
        return Integer.parseInt(contents.nextLine());
    } finally {
        if (contents != null) {
            contents.close();
        }
    }
}

Hier zeigt der __finally-Block an, welchen Code Java ausführen soll, unabhängig davon, was beim Lesen der Datei geschieht.

Selbst wenn eine FileNotFoundException im Aufrufstapel ausgelöst wird, ruft Java den Inhalt von finally auf, bevor dies geschieht.

Wir können auch die Ausnahme and behandeln und sicherstellen, dass unsere Ressourcen geschlossen werden:

public int getPlayerScore(String playerFile) {
    Scanner contents;
    try {
        contents = new Scanner(new File(playerFile));
        return Integer.parseInt(contents.nextLine());
    } catch (FileNotFoundException noFile ) {
        logger.warn("File not found, resetting score.");
        return 0;
    } finally {
        try {
            if (contents != null) {
                contents.close();
            }
        } catch (IOException io) {
            logger.error("Couldn't close the reader!", io);
        }
    }
}
  • Da close auch eine "riskante" Methode ist, müssen wir auch deren Ausnahme feststellen! **

Das mag ziemlich kompliziert aussehen, aber wir brauchen jedes Teil, um jedes mögliche Problem richtig zu behandeln.

4.4. try -with-resources

Glücklicherweise können wir ab Java 7 die obige Syntax vereinfachen, wenn Sie mit Elementen arbeiten, die AutoCloseable erweitern:

public int getPlayerScore(String playerFile) {
    try (Scanner contents = new Scanner(new File(playerFile))) {
      return Integer.parseInt(contents.nextLine());
    } catch (FileNotFoundException e ) {
      logger.warn("File not found, resetting score.");
      return 0;
    }
}

Wenn wir Referenzen, die __AutoClosable in der Try __deklaration enthalten, platzieren, müssen wir die Ressource nicht selbst schließen.

Wir können immer noch einen finally -Block verwenden, um jede andere Art von Bereinigung durchzuführen, die wir wollen.

Weitere Informationen finden Sie in unserem Artikel zum Verknüpfen:/java-try-with-resources[ try -with-resources].

4.5. Mehrere catch -Blöcke

Manchmal kann der Code mehr als eine Ausnahme auslösen, und wir können mehr als einen einzelnen catch -Block behandeln:

public int getPlayerScore(String playerFile) {
    try (Scanner contents = new Scanner(new File(playerFile))) {
        return Integer.parseInt(contents.nextLine());
    } catch (IOException e) {
        logger.warn("Player file wouldn't load!", e);
        return 0;
    } catch (NumberFormatException e) {
        logger.warn("Player file was corrupted!", e);
        return 0;
    }
}

Mehrere Fänge geben uns die Möglichkeit, jede Ausnahme anders zu behandeln, falls dies erforderlich sein sollte.

Beachten Sie auch hier, dass wir FileNotFoundException nicht abfangen, und zwar weil IOException erweitert wird. Da wir IOException__ abfangen, wird Java berücksichtigen, dass jede seiner Unterklassen auch behandelt wird.

Nehmen wir jedoch an, dass wir _FileNotFoundException anders als die allgemeinere IOException_ behandeln müssen:

public int getPlayerScore(String playerFile) {
    try (Scanner contents = new Scanner(new File(playerFile)) ) {
        return Integer.parseInt(contents.nextLine());
    } catch (FileNotFoundException e) {
        logger.warn("Player file not found!", e);
        return 0;
    } catch (IOException e) {
        logger.warn("Player file wouldn't load!", e);
        return 0;
    } catch (NumberFormatException e) {
        logger.warn("Player file was corrupted!", e);
        return 0;
    }
}

Mit Java können wir Unterklassenausnahmen separat behandeln. Denken Sie daran, sie in der Liste der Fänge höher zu platzieren.

4.6. Union Catch -Blöcke

Wenn wir wissen, dass die Art und Weise, wie wir mit Fehlern umgehen, dieselbe sein wird, hat Java 7 die Möglichkeit eingeführt, mehrere Ausnahmen in demselben Block abzufangen:

public int getPlayerScore(String playerFile) {
    try (Scanner contents = new Scanner(new File(playerFile))) {
        return Integer.parseInt(contents.nextLine());
    } catch (IOException | NumberFormatException e) {
        logger.warn("Failed to load score!", e);
        return 0;
    }
}

5. Ausnahmen auslösen

Wenn wir die Ausnahme nicht selbst behandeln möchten oder unsere Ausnahmen generieren möchten, müssen andere Benutzer das throw -Schlüsselwort kennen.

Nehmen wir an, wir haben die folgende geprüfte Ausnahme, die wir selbst erstellt haben:

public class TimeoutException extends Exception {
    public TimeoutException(String message) {
        super(message);
    }
}

und wir haben eine Methode, die möglicherweise lange Zeit in Anspruch nehmen kann:

public List<Player> loadAllPlayers(String playersFile) {
   //... potentially long operation
}

5.1. Eine geprüfte Ausnahme auslösen

So wie wir von einer Methode zurückkehren, können wir an jedem Punkt _throw _ .

Natürlich sollten wir werfen, wenn wir zeigen wollen, dass etwas schief gegangen ist:

public List<Player> loadAllPlayers(String playersFile) throws TimeoutException {
    while ( !tooLong ) {
       //... potentially long operation
    }
    throw new TimeoutException("This operation took too long");
}

Da TimeoutException aktiviert ist, müssen wir auch das Schlüsselwort throws in der Signatur verwenden, damit Aufrufer unserer Methode wissen, wie sie damit umgehen sollen.

5.2. __Throw __ing eine ungeprüfte Ausnahme

Wenn wir zum Beispiel die Eingabe von Eingaben überprüfen möchten, können wir stattdessen eine ungeprüfte Ausnahme verwenden:

public List<Player> loadAllPlayers(String playersFile) throws TimeoutException {
    if(!isFilenameValid(playersFile)) {
        throw new IllegalArgumentException("Filename isn't valid!");
    }

   //...
}

Da IllegalArgumentException nicht markiert ist, müssen wir die Methode nicht markieren, obwohl wir dies gerne tun.

Einige kennzeichnen die Methode trotzdem als eine Art Dokumentation.

** 5.3. Umwickeln und erneut werfen

Wir können uns auch dafür entscheiden, eine Ausnahme erneut zu werfen, die wir erwischt haben:

public List<Player> loadAllPlayers(String playersFile)
  throws IOException {
    try {
       //...
    } catch (IOException io) {
        throw io;
    }
}

Oder machen Sie einen Wrap und wiederholen Sie:

public List<Player> loadAllPlayers(String playersFile)
  throws PlayerLoadException {
    try {
       //...
    } catch (IOException io) {
        throw new PlayerLoadException(io);
    }
}

Dies kann nützlich sein, um viele verschiedene Ausnahmen in einer zusammenzufassen.

5.4. Throwable oder Exception erneut werfen

Nun zu einem besonderen Fall.

Wenn die einzigen möglichen Ausnahmen, die ein bestimmter Codeblock auslösen könnte, unchecked -Ausnahmen sind, können wir Throwable oder _Exception _ fangen und erneut auslösen, ohne sie unserer Methodensignatur hinzuzufügen:

public List<Player> loadAllPlayers(String playersFile) {
    try {
        throw new NullPointerException();
    } catch (Throwable t) {
        throw t;
    }
}

Obwohl der obige Code einfach ist, kann er keine geprüfte Ausnahmebedingung auslösen, und deshalb müssen wir die Signatur nicht mit einer __throws __clause kennzeichnen, obwohl wir eine geprüfte Ausnahmebedingung erneut erstellen.

  • Dies ist praktisch für Proxy-Klassen und -Methoden. ** Weitere Informationen hierzu finden Sie unter hier .

5,5. Erbe

Wenn wir Methoden mit einem throws -Schlüsselwort markieren, wirkt sich dies darauf aus, wie Unterklassen unsere Methode überschreiben können.

Wenn unsere Methode eine geprüfte Ausnahme auslöst:

public class Exceptions {
    public List<Player> loadAllPlayers(String playersFile)
      throws TimeoutException {
       //...
    }
}

Eine Unterklasse kann eine "weniger riskante" Signatur haben:

public class FewerExceptions extends Exceptions {
    @Override
    public List<Player> loadAllPlayers(String playersFile) {
       //overridden
    }
}

Aber keine " __more __riskier" Signatur:

public class MoreExceptions extends Exceptions {
    @Override
    public List<Player> loadAllPlayers(String playersFile) throws MyCheckedException {
       //overridden
    }
}

Dies liegt daran, dass Verträge zur Kompilierzeit durch den Referenztyp bestimmt werden. Wenn ich eine Instanz von _MoreExceptions de und sie in Exceptions_ speichere:

Exceptions exceptions = new MoreExceptions();
exceptions.loadAllPlayers("file");

Dann wird mir die JVM nur sagen, catch die TimeoutException zu erfassen, was falsch ist, da ich gesagt habe, dass MoreExceptions # loadAllPlayers eine andere Ausnahme auslöst.

  • Einfach ausgedrückt: Unterklassen können weniger ausgecheckte Ausnahmen als ihre Oberklasse ausgeben, jedoch nicht mehr . **

6. Anti-Patterns

6.1. Ausnahmen schlucken

Nun gibt es noch eine andere Möglichkeit, den Compiler zufrieden zu stellen:

public int getPlayerScore(String playerFile) {
    try {
       //...
    } catch (Exception e) {}//<== catch and swallow
    return 0;
}
  • Das Obige wird als Ausnahme bezeichnet ** bezeichnet. Die meiste Zeit wäre es für uns etwas gemein, dies zu tun, da es nicht das Problem anspricht und es verhindert, dass auch anderer Code das Problem lösen kann.

Es gibt Zeiten, in denen es eine geprüfte Ausnahme gibt, von der wir überzeugt sind, dass sie niemals passieren wird. In diesen Fällen sollten wir immer noch einen Kommentar hinzufügen, der besagt, dass wir die Ausnahme absichtlich gegessen haben :

public int getPlayerScore(String playerFile) {
    try {
       //...
    } catch (IOException e) {
       //this will never happen
    }
}

Eine andere Möglichkeit, eine Ausnahme zu "schlucken", besteht im einfachen Ausdruck der Ausnahme im Fehlerstrom:

public int getPlayerScore(String playerFile) {
    try {
       //...
    } catch (Exception e) {
        e.printStackTrace();
    }
    return 0;
}

Wir haben unsere Situation ein wenig verbessert, indem wir den Fehler mindestens zur späteren Diagnose irgendwo aufschrieben

Es wäre jedoch besser für uns, einen Logger zu verwenden:

public int getPlayerScore(String playerFile) {
    try {
       //...
    } catch (IOException e) {
        logger.error("Couldn't load the score", e);
        return 0;
    }
}

Es ist zwar sehr bequem, Ausnahmen auf diese Weise zu behandeln, wir müssen jedoch sicherstellen, dass wir keine wichtigen Informationen verschlucken, die Anrufer unseres Codes zur Behebung des Problems verwenden könnten.

Schließlich können wir eine Ausnahme versehentlich verschlucken, indem wir sie nicht als Ursache betrachten, wenn wir eine neue Ausnahme auslösen:

public int getPlayerScore(String playerFile) {
    try {
       //...
    } catch (IOException e) {
        throw new PlayerScoreException();
    }
}

Hier klopfen wir uns auf den Rücken, um unseren Anrufer auf einen Fehler aufmerksam zu machen, aber wir nehmen nicht die IOException als Ursache an. Aus diesem Grund haben wir wichtige Informationen verloren, mit denen Anrufer oder Operatoren das Problem diagnostizieren könnten.

Wir sind besser dran:

public int getPlayerScore(String playerFile) {
    try {
       //...
    } catch (IOException e) {
        throw new PlayerScoreException(e);
    }
}

Beachten Sie den subtilen Unterschied zwischen IOException als cause von PlayerScoreException .

6.2. Return in einem finally -Block verwenden

Eine andere Möglichkeit, Ausnahmen zu schlucken, besteht darin, aus dem final -Block zurückzukehren. Dies ist schlecht, da die JVM bei einer abrupten Rückkehr die Ausnahme verwirft, selbst wenn sie von unserem Code abgeworfen wurde:

public int getPlayerScore(String playerFile) {
    int score = 0;
    try {
        throw new IOException();
    } finally {
        return score;//<== the IOException is dropped
    }
}

Wenn die Ausführung des try-Blocks aus irgendeinem anderen Grund R__ abrupt beendet wird, wird der finally-Block ausgeführt, und dann gibt es eine Auswahl.

Wenn der finally -Block normal abgeschlossen wird, wird die try-Anweisung aus Gründen R abrupt beendet.

Wenn der finally -Block aus Gründen S abrupt abgeschlossen wird, wird die try-Anweisung aus Gründen S abrupt beendet (und der Grund R wird verworfen).

==== 6.3. Throw in einem finally -Block verwenden

Ähnlich wie bei der Verwendung von return in einem finally -Block hat die in einem finally -Block geworfene Ausnahme Vorrang vor der im catch-Block auftretenden Ausnahme.

Dadurch wird die ursprüngliche Ausnahme aus dem try -Block gelöscht, und wir verlieren all diese wertvollen Informationen:

public int getPlayerScore(String playerFile) {
    try {
       //...
    } catch ( IOException io ) {
        throw new IllegalStateException(io);//<== eaten by the finally
    } finally {
        throw new OtherException();
    }
}

==== 6.4. Throw als goto verwenden

Einige Leute gaben auch der Versuchung nach, throw als goto -Anweisung zu verwenden:

public void doSomething() {
    try {
       //bunch of code
        throw new MyException();
       //second bunch of code
    } catch (MyException e) {
       //third bunch of code
    }
}

Dies ist ungerade, da der Code versucht, im Gegensatz zur Fehlerbehandlung Ausnahmen für die Flusssteuerung zu verwenden.

=== 7. Häufige Ausnahmen und Fehler

Hier einige Ausnahmen und Fehler, auf die wir alle gelegentlich stoßen:

==== 7.1. Geprüfte Ausnahmen

  • IOException - Diese Ausnahme ist normalerweise eine Möglichkeit, dies zu sagen

etwas im Netzwerk, Dateisystem oder in der Datenbank ist fehlgeschlagen.

==== 7.2. Laufzeitausnahmen

  • ArrayIndexOutOfBoundsException - Diese Ausnahme bedeutet, dass wir es versucht haben

Zugriff auf einen nicht vorhandenen Array-Index, z. B. wenn versucht wird, Index 5 aus einem Array der Länge 3 abzurufen

  • ClassCastException – Diese Ausnahme bedeutet, dass wir versucht haben, das auszuführen

eine illegale Besetzung, wie der Versuch, einen String in eine List umzuwandeln. Wir können es normalerweise vermeiden, indem wir vor dem Casting Abwehrmaßnahmen durchführen.

  • IllegalArgumentException - Diese Ausnahme ist ein generischer Weg für uns

sagen Sie, dass einer der angegebenen Methoden- oder Konstruktorparameter ungültig ist.

  • IllegalStateException - Diese Ausnahme ist ein generischer Weg für uns

sagen Sie, dass unser innerer Zustand ebenso wie der Zustand unseres Objekts ungültig ist.

  • NullPointerException - Diese Ausnahme bedeutet, dass wir versucht haben, auf a zu verweisen

null object. Wir können es normalerweise vermeiden, indem wir entweder defensiv arbeiten null prüft oder mit Optional. ** NumberFormatException - Diese Ausnahme bedeutet, dass wir es versucht haben

konvertieren Sie einen String in eine Zahl, aber die Zeichenfolge enthält unzulässige Zeichen, wie beispielsweise der Versuch, "5f3" in eine Zahl umzuwandeln.

==== 7.3. Fehler

  • StackOverflowError – Diese Ausnahme bedeutet, dass der Stack-Trace vorhanden ist

zu groß. Dies kann manchmal in massiven Anwendungen passieren. Dies bedeutet jedoch normalerweise, dass in unserem Code eine unendliche Rekursion stattfindet.

  • NoClassDefFoundError - Diese Ausnahme bedeutet, dass eine Klasse nicht erfolgreich war

load entweder aufgrund eines fehlenden Klassenpfads oder aufgrund eines Fehlers bei der statischen Initialisierung.

  • OutOfMemoryError - Diese Ausnahme bedeutet, dass die JVM nicht vorhanden ist

mehr Speicher verfügbar, um weitere Objekte zuzuordnen. Dies liegt manchmal an einem Speicherverlust.

=== 8. Fazit

In diesem Artikel haben wir die Grundlagen der Ausnahmebehandlung sowie einige Beispiele guter und schlechter Praxis erläutert.

Wie immer kann der gesamte in diesem Artikel enthaltene Code auf GitHub unter https://github.com/eugenp/tutorials/tree/master/core-java-lang [over gefunden werden!