Skip to content
IRC-Coding IRC-Coding
Exception Handling Java Try Catch Finally Throw Throws Custom Exceptions Fehlerbehandlung

Exception Handling in Java: Try-Catch-Finally, Throw, Throws & Custom Exceptions

Java Exception Handling mit try-catch-finally, throw, throws und custom exceptions. Fehlerbehandlung, Exception-Hierarchie, Best Practices und robuste Softwareentwicklung.

S

schutzgeist

2 min read

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

  1. Exception-Hierarchie: Throwable, Error, Exception, RuntimeException
  2. Try-Catch-Finally: Fehlerbehandlungsstruktur
  3. Throw/Throws: Exception-Auslösung und -deklaration
  4. Custom Exceptions: Spezifische Fehlerklassen
  5. Try-with-Resources: Automatische Ressourcenverwaltung
  6. Exception Chaining: Verknüpfung von Exceptions
  7. Best Practices: Richtlinien für Exception Handling
  8. 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

  1. Was ist der Unterschied zwischen checked und unchecked exceptions? Checked Exceptions müssen deklariert/behandelt werden, unchecked nicht (RuntimeException).

  2. Wann wird finally ausgeführt? Immer, egal ob Exception auftritt oder nicht, auch bei return im try-Block.

  3. Erklären Sie try-with-resources! Automatische Ressourcenbereinigung für Objekte, die AutoCloseable implementieren.

  4. Was ist Exception Chaining? Weitergeben von Exceptions unter Beibehaltung der ursprünglichen Ursache.

Wichtigste Quellen

  1. https://docs.oracle.com/javase/tutorial/essential/exceptions/
  2. https://www.baeldung.com/java-exceptions
  3. https://effectivejava.com/
Zurück zum Blog
Share:

Ähnliche Beiträge