Exception Handling in Java: Try-Catch-Finally, Throw, Throws & Custom Exceptions
Dieser Beitrag ist eine umfassende Anleitung zum Java Exception Handling – inklusive try-catch-finally, throw, throws und custom exceptions mit praktischen Beispielen.
In a Nutshell
Exception Handling ermöglicht kontrollierte Fehlerbehandlung in Java. try-catch-finally fängt Exceptions, throw/throws deklariert sie, und custom Exceptions ermöglichen spezifische Fehlerbehandlung.
Kompakte Fachbeschreibung
Exception Handling ist ein Mechanismus zur Behandlung von Fehlern und abnormalen Bedingungen während der Programmausführung. Java verwendet eine strukturierte Exception-Hierarchie.
Exception-Hierarchie:
- Throwable: Basisklasse aller Errors und Exceptions
- Error: Schwerwiegende Systemfehler (nicht behandelbar)
- Exception: Behandelbare Ausnahmen
- Checked Exceptions: Kompilierzeit-Prüfung erforderlich
- Unchecked Exceptions: Laufzeitfehler (RuntimeException)
Wichtige Konzepte:
Try-Catch-Finally
- try: Block mit potenziell fehlerhaftem Code
- catch: Block zur Behandlung spezifischer Exceptions
- finally: Block, der immer ausgeführt wird (auch bei Exception)
- try-with-resources: Automatische Ressourcenbereinigung
Throw und Throws
- throw: Explizites Auslösen einer Exception
- throws: Deklaration von Exceptions in Methodensignatur
- rethrow: Weitergeben von Exceptions
Custom Exceptions
- Eigene Exception-Klassen: Spezifische Fehlerbehandlung
- Business Exceptions: Domänenspezifische Fehler
- Validation Exceptions: Eingabevalidierungsfehler
Prüfungsrelevante Stichpunkte
- Exception Handling: Strukturierte Fehlerbehandlung in Java
- Try-Catch-Finally: Grundlegende Fehlerbehandlungsblöcke
- Checked vs Unchecked Exceptions: Kompilierzeit- vs Laufzeitfehler
- Throw vs Throws: Auslösen vs Deklarieren von Exceptions
- Custom Exceptions: Eigene Exception-Klassen erstellen
- Exception-Hierarchie: Throwable → Error/Exception → RuntimeException
- IHK-relevant: Wichtig für robuste, fehlerresistente Software
Kernkomponenten
- Exception-Hierarchie: Throwable, Error, Exception, RuntimeException
- Try-Catch-Finally: Fehlerbehandlungsstruktur
- Throw/Throws: Exception-Auslösung und -deklaration
- Custom Exceptions: Spezifische Fehlerklassen
- Try-with-Resources: Automatische Ressourcenverwaltung
- Exception Chaining: Verknüpfung von Exceptions
- Best Practices: Richtlinien für Exception Handling
- Logging: Protokollierung von Fehlern
Praxisbeispiele
1. Grundlegendes Exception Handling
import java.io.*;
import java.util.*;
public class ExceptionGrundlagen {
public static void main(String[] args) {
System.out.println("=== Exception Handling Grundlagen ===");
// Einfache try-catch
einfachesTryCatch();
// Mehrere catch-Blöcke
mehrfachCatch();
// Finally-Block
finallyDemo();
// Try-with-Resources
tryWithResourcesDemo();
// Exception-Auslösung
exceptionAusloesen();
}
private static void einfachesTryCatch() {
System.out.println("\n--- Einfaches Try-Catch ---");
try {
// Potentiell fehlerhafter Code
int ergebnis = 10 / 0; // ArithmeticException
System.out.println("Ergebnis: " + ergebnis);
} catch (ArithmeticException e) {
System.out.println("Fehler: Division durch Null");
System.out.println("Exception-Typ: " + e.getClass().getSimpleName());
System.out.println("Nachricht: " + e.getMessage());
}
System.out.println("Programm läuft weiter");
}
private static void mehrfachCatch() {
System.out.println("\n--- Mehrfache Catch-Blöcke ---");
String[] zahlen = {"10", "5", "abc", "2"};
for (String zahlString : zahlen) {
try {
int zahl = Integer.parseInt(zahlString);
int ergebnis = 100 / zahl;
System.out.println("100 / " + zahl + " = " + ergebnis);
} catch (NumberFormatException e) {
System.out.println("Fehler: '" + zahlString + "' ist keine Zahl");
} catch (ArithmeticException e) {
System.out.println("Fehler: Division durch Null bei " + zahlString);
} catch (Exception e) {
// Allgemeiner Exception-Handler
System.out.println("Unerwarteter Fehler: " + e.getMessage());
}
}
}
private static void finallyDemo() {
System.out.println("\n--- Finally-Block Demo ---");
BufferedReader reader = null;
try {
// Ressource öffnen
reader = new BufferedReader(new FileReader("nichtexistente.txt"));
String zeile = reader.readLine();
System.out.println("Gelesen: " + zeile);
} catch (FileNotFoundException e) {
System.out.println("Datei nicht gefunden: " + e.getMessage());
} catch (IOException e) {
System.out.println("Lesefehler: " + e.getMessage());
} finally {
// Wird immer ausgeführt
System.out.println("Finally-Block wird ausgeführt");
if (reader != null) {
try {
reader.close();
System.out.println("Reader geschlossen");
} catch (IOException e) {
System.out.println("Fehler beim Schließen: " + e.getMessage());
}
}
}
}
private static void tryWithResourcesDemo() {
System.out.println("\n--- Try-with-Resources Demo ---");
// Automatische Ressourcenverwaltung
try (BufferedReader reader = new BufferedReader(new FileReader("beispiel.txt"))) {
String zeile;
int zeilenNummer = 1;
while ((zeile = reader.readLine()) != null) {
System.out.println("Zeile " + zeilenNummer + ": " + zeile);
zeilenNummer++;
if (zeilenNummer > 3) {
throw new IOException("Künstlicher Fehler nach 3 Zeilen");
}
}
} catch (FileNotFoundException e) {
System.out.println("Datei nicht gefunden: " + e.getMessage());
} catch (IOException e) {
System.out.println("IO-Fehler: " + e.getMessage());
} finally {
System.out.println("Try-with-Resources beendet (Reader automatisch geschlossen)");
}
}
private static void exceptionAusloesen() {
System.out.println("\n--- Exception-Auslösung ---");
try {
validiereAlter(15); // Löst Exception aus
} catch (IllegalArgumentException e) {
System.out.println("Validierungsfehler: " + e.getMessage());
}
try {
validiereAlter(25); // Kein Fehler
} catch (IllegalArgumentException e) {
System.out.println("Dies sollte nicht passieren: " + e.getMessage());
}
System.out.println("Alter-Validierung abgeschlossen");
}
private static void validiereAlter(int alter) {
if (alter < 18) {
throw new IllegalArgumentException("Alter muss mindestens 18 sein, aber war: " + alter);
}
System.out.println("Alter " + alter + " ist gültig");
}
}
2. Custom Exceptions und throws-Klausel
// Custom Exception-Klassen
class GeschaeftsException extends Exception {
public GeschaeftsException(String nachricht) {
super(nachricht);
}
public GeschaeftsException(String nachricht, Throwable ursache) {
super(nachricht, ursache);
}
}
class ValidierungsException extends RuntimeException {
private String feld;
public ValidierungsException(String feld, String nachricht) {
super(nachricht);
this.feld = feld;
}
public String getFeld() {
return feld;
}
}
class DatenbankException extends Exception {
private int fehlercode;
public DatenbankException(String nachricht, int fehlercode) {
super(nachricht);
this.fehlercode = fehlercode;
}
public int getFehlercode() {
return fehlercode;
}
}
// Service-Klassen mit Exception Handling
class KundenService {
// Methode mit throws-Klausel
public Kunde findeKunde(int kundenId) throws GeschaeftsException, DatenbankException {
try {
// Simuliere Datenbankzugriff
if (kundenId < 1000) {
throw new DatenbankException("Ungültige Kunden-ID: " + kundenId, 404);
}
if (kundenId == 1234) {
throw new DatenbankException("Datenbankverbindung fehlgeschlagen", 500);
}
// Simuliere erfolgreichen Zugriff
return new Kunde(kundenId, "Max Mustermann", "max@example.com");
} catch (SQLException e) {
// Exception Chaining - Original-Exception bewahren
throw new DatenbankException("Datenbankfehler bei Kundensuche", 503);
}
}
// Methode mit Custom Exception
public void kundeAnlegen(Kunde kunde) throws GeschaeftsException {
try {
validiereKunde(kunde);
// Simuliere Geschäftslogik
if (kunde.getName().toLowerCase().contains("test")) {
throw new GeschaeftsException("Test-Kunden dürfen nicht angelegt werden");
}
// Kunde würde hier in Datenbank gespeichert
System.out.println("Kunde angelegt: " + kunde.getName());
} catch (ValidierungsException e) {
// Re-throw mit zusätzlicher Information
throw new GeschaeftsException("Kundendaten ungültig: " + e.getMessage(), e);
}
}
private void validiereKunde(Kunde kunde) {
if (kunde.getName() == null || kunde.getName().trim().isEmpty()) {
throw new ValidierungsException("name", "Name darf nicht leer sein");
}
if (kunde.getEmail() == null || !kunde.getEmail().contains("@")) {
throw new ValidierungsException("email", "Ungültige E-Mail-Adresse");
}
if (kunde.getName().length() > 100) {
throw new ValidierungsException("name", "Name zu lang (max 100 Zeichen)");
}
}
}
// Datenmodell
class Kunde {
private int id;
private String name;
private String email;
public Kunde(int id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// Getter
public int getId() { return id; }
public String getName() { return name; }
public String getEmail() { return email; }
}
// Hauptklasse für Demo
public class CustomExceptionDemo {
public static void main(String[] args) {
System.out.println("=== Custom Exceptions Demo ===");
KundenService service = new KundenService();
// Demo 1: Erfolgreiche Kundensuche
try {
Kunde kunde = service.findeKunde(1001);
System.out.println("Kunde gefunden: " + kunde.getName());
} catch (GeschaeftsException | DatenbankException e) {
System.out.println("Fehler bei Kundensuche: " + e.getMessage());
}
// Demo 2: Datenbankfehler
try {
service.findeKunde(999);
} catch (GeschaeftsException e) {
System.out.println("Geschäftsfehler: " + e.getMessage());
} catch (DatenbankException e) {
System.out.println("Datenbankfehler (Code " + e.getFehlercode() + "): " + e.getMessage());
}
// Demo 3: Validierungsfehler beim Anlegen
try {
Kunde ungueltigerKunde = new Kunde(0, "", "ungueltig@");
service.kundeAnlegen(ungueltigerKunde);
} catch (GeschaeftsException e) {
System.out.println("Fehler beim Kundenanlegen: " + e.getMessage());
// Ursprungs-Exception anzeigen
if (e.getCause() instanceof ValidierungsException) {
ValidierungsException ve = (ValidierungsException) e.getCause();
System.out.println("Validierungsfehler in Feld '" + ve.getFeld() + "'");
}
}
// Demo 4: Business Rule Exception
try {
Kunde testKunde = new Kunde(0, "Test User", "test@example.com");
service.kundeAnlegen(testKunde);
} catch (GeschaeftsException e) {
System.out.println("Business Rule Fehler: " + e.getMessage());
}
// Demo 5: Exception Logging
exceptionLoggingDemo();
}
private static void exceptionLoggingDemo() {
System.out.println("\n--- Exception Logging Demo ---");
try {
// Simuliere komplexe Operation
komplexeOperation();
} catch (Exception e) {
// Strukturiertes Logging
logException(e);
}
}
private static void komplexeOperation() throws Exception {
try {
// Erste Operation
ersteOperation();
// Zweite Operation
zweiteOperation();
} catch (IllegalArgumentException e) {
// Exception mit Kontextinformationen weiterreichen
throw new Exception("Fehler in komplexer Operation", e);
}
}
private static void ersteOperation() {
if (Math.random() > 0.5) {
throw new IllegalArgumentException("Fehler in erster Operation");
}
System.out.println("Erste Operation erfolgreich");
}
private static void zweiteOperation() {
if (Math.random() > 0.7) {
throw new IllegalArgumentException("Fehler in zweiter Operation");
}
System.out.println("Zweite Operation erfolgreich");
}
private static void logException(Exception e) {
System.out.println("=== Exception Log ===");
System.out.println("Typ: " + e.getClass().getSimpleName());
System.out.println("Nachricht: " + e.getMessage());
System.out.println("Stack Trace:");
// Stack Trace ausgeben
for (StackTraceElement element : e.getStackTrace()) {
System.out.println(" at " + element.getClassName() +
"." + element.getMethodName() +
"(" + element.getFileName() +
":" + element.getLineNumber() + ")");
}
// Ursprungs-Exception loggen
if (e.getCause() != null) {
System.out.println("Ursache: " + e.getCause().getClass().getSimpleName() +
" - " + e.getCause().getMessage());
}
}
}
3. Fortgeschrittene Exception Handling Patterns
import java.util.*;
import java.util.function.*;
public class AdvancedExceptionPatterns {
// Result Pattern für Fehlerbehandlung ohne Exceptions
static class Result<T> {
private final T value;
private final Exception error;
private final boolean success;
private Result(T value, Exception error, boolean success) {
this.value = value;
this.error = error;
this.success = success;
}
public static <T> Result<T> success(T value) {
return new Result<>(value, null, true);
}
public static <T> Result<T> failure(Exception error) {
return new Result<>(null, error, false);
}
public boolean isSuccess() { return success; }
public boolean isFailure() { return !success; }
public T getValue() {
if (!success) {
throw new IllegalStateException("Kein Wert bei Fehler vorhanden");
}
return value;
}
public Exception getError() {
if (success) {
throw new IllegalStateException("Kein Fehler bei Erfolg vorhanden");
}
return error;
}
public <U> Result<U> map(Function<T, U> mapper) {
if (success) {
try {
return Result.success(mapper.apply(value));
} catch (Exception e) {
return Result.failure(e);
}
} else {
return Result.failure(error);
}
}
public <U> Result<U> flatMap(Function<T, Result<U>> mapper) {
if (success) {
try {
return mapper.apply(value);
} catch (Exception e) {
return Result.failure(e);
}
} else {
return Result.failure(error);
}
}
public T orElse(T defaultValue) {
return success ? value : defaultValue;
}
}
// Exception-Wrapper für Functional Interfaces
@FunctionalInterface
interface CheckedSupplier<T> {
T get() throws Exception;
}
@FunctionalInterface
interface CheckedRunnable {
void run() throws Exception;
}
@FunctionalInterface
interface CheckedFunction<T, R> {
R apply(T t) throws Exception;
}
// Utility-Methoden für Exception Handling
static class ExceptionUtils {
public static <T> Optional<T> optionalOf(CheckedSupplier<T> supplier) {
try {
return Optional.ofNullable(supplier.get());
} catch (Exception e) {
return Optional.empty();
}
}
public static <T> Result<T> resultOf(CheckedSupplier<T> supplier) {
try {
return Result.success(supplier.get());
} catch (Exception e) {
return Result.failure(e);
}
}
public static void unchecked(CheckedRunnable runnable) {
try {
runnable.run();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T, R> Function<T, R> uncheckedFunction(CheckedFunction<T, R> function) {
return t -> {
try {
return function.apply(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
public static <T> Supplier<T> uncheckedSupplier(CheckedSupplier<T> supplier) {
return () -> {
try {
return supplier.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
}
// Retry-Mechanismus
static class RetryUtils {
public static <T> T retry(int maxAttempts, long delayMs, CheckedSupplier<T> supplier)
throws Exception {
Exception lastException = null;
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return supplier.get();
} catch (Exception e) {
lastException = e;
if (attempt == maxAttempts) {
break;
}
System.out.println("Versuch " + attempt + " fehlgeschlagen, retry in " + delayMs + "ms");
Thread.sleep(delayMs);
}
}
throw new Exception("Alle " + maxAttempts + " Versuche fehlgeschlagen", lastException);
}
public static <T> Optional<T> retryOptional(int maxAttempts, long delayMs,
CheckedSupplier<T> supplier) {
try {
return Optional.of(retry(maxAttempts, delayMs, supplier));
} catch (Exception e) {
return Optional.empty();
}
}
}
// Service-Klassen mit fortgeschrittenen Patterns
static class DatenbankService {
// Mit Result Pattern
public Result<String> leseDatenMitResult(String id) {
try {
// Simuliere Datenbankzugriff
if (id == null || id.isEmpty()) {
return Result.failure(new IllegalArgumentException("ID darf nicht leer sein"));
}
if (id.equals("error")) {
return Result.failure(new RuntimeException("Datenbankfehler"));
}
String daten = "Daten für " + id;
return Result.success(daten);
} catch (Exception e) {
return Result.failure(e);
}
}
// Mit Optional
public Optional<String> leseDatenMitOptional(String id) {
return ExceptionUtils.optionalOf(() -> {
if (id == null || id.isEmpty()) {
throw new IllegalArgumentException("ID darf nicht leer sein");
}
if (id.equals("notfound")) {
return null;
}
return "Daten für " + id;
});
}
// Mit Retry
public String leseDatenMitRetry(String id) throws Exception {
return RetryUtils.retry(3, 1000, () -> {
// Simuliere instabile Verbindung
if (Math.random() > 0.7) {
throw new RuntimeException("Verbindungsfehler");
}
return "Stabile Daten für " + id;
});
}
}
public static void main(String[] args) {
System.out.println("=== Fortgeschrittene Exception Patterns ===");
DatenbankService service = new DatenbankService();
// Result Pattern Demo
System.out.println("\n--- Result Pattern Demo ---");
Result<String> result1 = service.leseDatenMitResult("123");
System.out.println("Erfolgreich: " + result1.isSuccess());
result1.ifPresent(value -> System.out.println("Wert: " + value));
Result<String> result2 = service.leseDatenMitResult("error");
System.out.println("Erfolgreich: " + result2.isSuccess());
result2.ifPresentOrElse(
value -> System.out.println("Wert: " + value),
error -> System.out.println("Fehler: " + error.getMessage())
);
// Result Chaining
Result<Integer> laenge = result1
.map(String::length)
.map(laeng -> laeng * 2);
System.out.println("Verdoppelte Länge: " + laenge.orElse(0));
// Optional Pattern Demo
System.out.println("\n--- Optional Pattern Demo ---");
Optional<String> opt1 = service.leseDatenMitOptional("123");
opt1.ifPresent(daten -> System.out.println("Gefunden: " + daten));
Optional<String> opt2 = service.leseDatenMitOptional("notfound");
System.out.println("Gefunden: " + opt2.isPresent());
// Optional mit Default
String ergebnis = opt2.orElse("Standardwert");
System.out.println("Ergebnis: " + ergebnis);
// Retry Demo
System.out.println("\n--- Retry Demo ---");
try {
String daten = service.leseDatenMitRetry("123");
System.out.println("Erfolg nach Retry: " + daten);
} catch (Exception e) {
System.out.println("Alle Retrys fehlgeschlagen: " + e.getMessage());
}
// Exception Utils Demo
System.out.println("\n--- Exception Utils Demo ---");
// Unchecked Functional Interface
List<String> zahlen = Arrays.asList("10", "5", "abc", "2");
zahlen.stream()
.map(ExceptionUtils.uncheckedFunction(zahl -> Integer.parseInt(zahl) * 2))
.forEach(ergebnis -> System.out.println("Verdoppelt: " + ergebnis));
// Exception Logging mit Context
System.out.println("\n--- Exception mit Context ---");
try {
berechneKomplex(10, 0);
} catch (Exception e) {
logWithContext(e, Map.of(
"operation", "berechneKomplex",
"param1", 10,
"param2", 0,
"timestamp", System.currentTimeMillis()
));
}
}
private static int berechneKomplex(int a, int b) throws Exception {
if (b == 0) {
throw new ArithmeticException("Division durch Null");
}
return a / b;
}
private static void logWithContext(Exception e, Map<String, Object> context) {
System.out.println("=== Exception mit Context ===");
System.out.println("Exception: " + e.getClass().getSimpleName());
System.out.println("Nachricht: " + e.getMessage());
System.out.println("Context:");
context.forEach((key, value) ->
System.out.println(" " + key + ": " + value));
System.out.println("Stack Trace:");
Arrays.stream(e.getStackTrace())
.limit(3)
.forEach(element ->
System.out.println(" at " + element.getClassName() +
"." + element.getMethodName()));
}
// Hilfsmethode für Result
private static <T> void ifPresent(Result<T> result, Consumer<T> action) {
if (result.isSuccess()) {
action.accept(result.getValue());
}
}
}
Exception-Hierarchie Übersicht
Throwable
├── Error (Systemfehler, nicht behandelbar)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── VirtualMachineError
└── Exception (Behandelbare Ausnahmen)
├── Checked Exceptions (Kompilierzeit-Prüfung)
│ ├── IOException
│ │ ├── FileNotFoundException
│ │ └── SQLException
│ ├── ClassNotFoundException
│ └── InterruptedException
└── RuntimeException (Unchecked Exceptions)
├── NullPointerException
├── IllegalArgumentException
├── ArithmeticException
├── IndexOutOfBoundsException
└── NumberFormatException
Best Practices für Exception Handling
DO’s
- Spezifische Exceptions fangen: Statt immer
Exception - Ressourcen bereinigen: Mit finally oder try-with-resources
- Meaningful Messages: Klare Fehlerbeschreibungen
- Exception Chaining: Ursprungs-Exception bewahren
- Logging: Exceptions protokollieren mit Kontext
DON’Ts
- Leere catch-Blöcke: Exceptions ignorieren
- Exception swallowing: Fehler unterdrücken
- Over-catch: Zu allgemeine Exceptions
- Return null: Statt Optional oder Result
- PrintStackTrace: In Produktivcode
Try-with-Resources vs Finally
Try-with-Resources (Modern)
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
// Ressourcen werden automatisch geschlossen
} catch (IOException e) {
// Exception handling
}
Finally-Block (Traditional)
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("file.txt"));
// Arbeit mit reader
} catch (IOException e) {
// Exception handling
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
// Fehler beim Schließen behandeln
}
}
}
Exception Handling Patterns
Template Method Pattern
public abstract class DatabaseTemplate {
public final Result<T> execute(CheckedSupplier<T> operation) {
try {
Connection conn = getConnection();
try {
return Result.success(operation.get());
} finally {
conn.close();
}
} catch (Exception e) {
return Result.failure(e);
}
}
protected abstract Connection getConnection() throws SQLException;
}
Decorator Pattern
public class RetryDecorator<T> implements Supplier<T> {
private final Supplier<T> supplier;
private final int maxRetries;
public RetryDecorator(Supplier<T> supplier, int maxRetries) {
this.supplier = supplier;
this.maxRetries = maxRetries;
}
@Override
public T get() {
Exception lastException = null;
for (int i = 0; i <= maxRetries; i++) {
try {
return supplier.get();
} catch (Exception e) {
lastException = e;
if (i < maxRetries) {
// Wait before retry
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException(ie);
}
}
}
}
throw new RuntimeException("All retries failed", lastException);
}
}
Vorteile und Nachteile
Vorteile von Exception Handling
- Fehlerkontrolle: Strukturierte Fehlerbehandlung
- Code-Klarheit: Trennung von Normal- und Fehlerfall
- Robustheit: Stabile Programme auch bei Fehlern
- Debugging: Bessere Fehleranalyse durch Stack-Traces
- Wartbarkeit: Zentrale Fehlerbehandlung
Nachteile
- Performance: Exception-Handling hat Overhead
- Komplexität: Verschachtelte try-catch-Strukturen
- Overhead: Mehr Code für Fehlerbehandlung
- Missbrauch: Exceptions für Kontrollfluss verwenden
Häufige Prüfungsfragen
-
Was ist der Unterschied zwischen checked und unchecked exceptions? Checked Exceptions müssen deklariert/behandelt werden, unchecked nicht (RuntimeException).
-
Wann wird finally ausgeführt? Immer, egal ob Exception auftritt oder nicht, auch bei return im try-Block.
-
Erklären Sie try-with-resources! Automatische Ressourcenbereinigung für Objekte, die AutoCloseable implementieren.
-
Was ist Exception Chaining? Weitergeben von Exceptions unter Beibehaltung der ursprünglichen Ursache.
Wichtigste Quellen
- https://docs.oracle.com/javase/tutorial/essential/exceptions/
- https://www.baeldung.com/java-exceptions
- https://effectivejava.com/