Eigene Exceptions
Eigene Exception-Klassen in Java erstellen: Custom Checked und Unchecked Exceptions, Exception-Hierarchien und Best Practices.
Eigene Exceptions erstellen
Die eingebauten Exceptions decken viele Faelle ab — aber manchmal brauchst du spezifischere Fehlermeldungen fuer deine Anwendung. Eine BankKontoLeerException ist viel aussagekraeftiger als eine generische RuntimeException. In diesem Kapitel lernst du, eigene Exceptions zu erstellen.
Warum eigene Exceptions?
// SCHLECHT: Generische Exception -- was genau ist schiefgelaufen?
throw new RuntimeException("Fehler bei der Bestellung");
// GUT: Spezifische eigene Exception
throw new BestellungNichtGefundenException("Bestellung #12345 nicht gefunden");
throw new KontostandZuNiedrigException(konto, betrag);
throw new UngueltigeEmailException("max@");
Eigene Exceptions bieten:
- Klarere Fehlermeldungen — was genau ist passiert?
- Gezielteres Fangen — verschiedene Fehler unterschiedlich behandeln
- Zusaetzliche Informationen — Kontostand, Bestellnummer, etc.
Eigene Unchecked Exception
Erweitere RuntimeException fuer Exceptions, die nicht gefangen werden muessen:
public class UngueltigesAlterException extends RuntimeException {
public UngueltigesAlterException(int alter) {
super("Ungueltiges Alter: " + alter + " (muss zwischen 0 und 150 liegen)");
}
}
public class Person {
private String name;
private int alter;
public Person(String name, int alter) {
if (alter < 0 || alter > 150) {
throw new UngueltigesAlterException(alter);
}
this.name = name;
this.alter = alter;
}
}
// Verwendung
var person = new Person("Max", 25); // OK
// var person2 = new Person("Anna", -5); // UngueltigesAlterException!
Eigene Checked Exception
Erweitere Exception fuer Exceptions, die gefangen werden muessen:
public class DateiNichtLesbarException extends Exception {
private final String dateipfad;
public DateiNichtLesbarException(String dateipfad) {
super("Datei nicht lesbar: " + dateipfad);
this.dateipfad = dateipfad;
}
public DateiNichtLesbarException(String dateipfad, Throwable ursache) {
super("Datei nicht lesbar: " + dateipfad, ursache);
this.dateipfad = dateipfad;
}
public String getDateipfad() {
return dateipfad;
}
}
public class DateiLeser {
public String leseDatei(String pfad) throws DateiNichtLesbarException {
try {
return Files.readString(Path.of(pfad));
} catch (IOException e) {
throw new DateiNichtLesbarException(pfad, e); // Ursache mitgeben!
}
}
}
// Verwendung: Muss gefangen werden!
var leser = new DateiLeser();
try {
var inhalt = leser.leseDatei("config.txt");
} catch (DateiNichtLesbarException e) {
System.out.println(e.getMessage());
System.out.println("Pfad: " + e.getDateipfad());
}
Exception mit zusaetzlichen Daten
Eigene Exceptions koennen beliebige Zusatzinformationen speichern:
public class KontostandZuNiedrigException extends RuntimeException {
private final double kontostand;
private final double gewuenscht;
public KontostandZuNiedrigException(double kontostand, double gewuenscht) {
super("Kontostand zu niedrig: %.2f EUR vorhanden, %.2f EUR gewuenscht"
.formatted(kontostand, gewuenscht));
this.kontostand = kontostand;
this.gewuenscht = gewuenscht;
}
public double getKontostand() { return kontostand; }
public double getGewuenscht() { return gewuenscht; }
public double getFehlbetrag() { return gewuenscht - kontostand; }
}
public class Bankkonto {
private double kontostand;
public void abheben(double betrag) {
if (betrag > kontostand) {
throw new KontostandZuNiedrigException(kontostand, betrag);
}
kontostand -= betrag;
}
}
// Verwendung
try {
konto.abheben(5000);
} catch (KontostandZuNiedrigException e) {
System.out.println(e.getMessage());
System.out.printf("Dir fehlen: %.2f EUR%n", e.getFehlbetrag());
}
Exception-Hierarchie erstellen
Fuer komplexere Anwendungen kannst du eine Hierarchie von Exceptions bauen:
// Basis-Exception fuer die gesamte Anwendung
public class ShopException extends RuntimeException {
public ShopException(String message) {
super(message);
}
public ShopException(String message, Throwable cause) {
super(message, cause);
}
}
// Spezifische Exceptions
public class ProduktNichtGefundenException extends ShopException {
private final String produktId;
public ProduktNichtGefundenException(String produktId) {
super("Produkt nicht gefunden: " + produktId);
this.produktId = produktId;
}
public String getProduktId() { return produktId; }
}
public class WarenkorbLeerException extends ShopException {
public WarenkorbLeerException() {
super("Der Warenkorb ist leer!");
}
}
public class LagerBestandException extends ShopException {
public LagerBestandException(String produkt, int verfuegbar, int gewuenscht) {
super("Nicht genug '%s' auf Lager: %d verfuegbar, %d gewuenscht"
.formatted(produkt, verfuegbar, gewuenscht));
}
}
// Verwendung: Gezieltes oder allgemeines Fangen
try {
shop.bestellen(warenkorb);
} catch (WarenkorbLeerException e) {
System.out.println("Bitte fuege erst Produkte hinzu!");
} catch (LagerBestandException e) {
System.out.println("Einige Produkte sind leider ausverkauft.");
} catch (ShopException e) {
// Faengt alle Shop-Exceptions auf
System.out.println("Shop-Fehler: " + e.getMessage());
}
Best Practices
1. Vier Standard-Konstruktoren
Professionelle Exceptions haben diese Konstruktoren:
public class MeineException extends RuntimeException {
// Ohne Nachricht
public MeineException() {
super();
}
// Mit Nachricht
public MeineException(String message) {
super(message);
}
// Mit Ursache
public MeineException(Throwable cause) {
super(cause);
}
// Mit Nachricht und Ursache
public MeineException(String message, Throwable cause) {
super(message, cause);
}
}
2. Exception-Ursache bewahren (Chaining)
// SCHLECHT: Original-Exception geht verloren
try {
verbindung.oeffnen();
} catch (SQLException e) {
throw new DatenbankException("Verbindung fehlgeschlagen");
// Wo genau war der Fehler? Keine Info!
}
// GUT: Original-Exception als Ursache mitgeben
try {
verbindung.oeffnen();
} catch (SQLException e) {
throw new DatenbankException("Verbindung fehlgeschlagen", e);
// e.getCause() liefert die Original-Exception
}
3. Checked vs. Unchecked — Wann was?
// Unchecked (RuntimeException): Programmierfehler, ungueltige Argumente
// --> Aufrufer kann das Problem durch besseren Code vermeiden
throw new IllegalArgumentException("Alter darf nicht negativ sein");
throw new UngueltigesAlterException(alter);
// Checked (Exception): Aeussere Faktoren, die nicht vermeidbar sind
// --> Aufrufer MUSS reagieren
throw new DateiNichtGefundenException(pfad);
throw new VerbindungsFehlerException(url);
4. Aussagekraeftige Nachrichten
// SCHLECHT:
throw new RuntimeException("Fehler");
throw new IllegalArgumentException("Ungueltig");
// GUT:
throw new RuntimeException("Benutzer 'max@mail.com' konnte nicht gespeichert werden");
throw new IllegalArgumentException("Alter muss zwischen 0 und 150 liegen, war: " + alter);
Vergleich mit Python
# Python: Eigene Exception
class AlterUngueltigError(ValueError):
def __init__(self, alter):
super().__init__(f"Ungueltiges Alter: {alter}")
self.alter = alter
// Java: Eigene Exception
public class AlterUngueltigException extends IllegalArgumentException {
private final int alter;
public AlterUngueltigException(int alter) {
super("Ungueltiges Alter: " + alter);
this.alter = alter;
}
public int getAlter() { return alter; }
}
Uebungen
Uebung 1: Passwort-Exception
Erstelle eine UngueltigesPasswortException mit Details, welche Regel verletzt wurde (zu kurz, keine Ziffer, kein Grossbuchstabe).
Uebung 2: Produkt-Exceptions
Erstelle eine Exception-Hierarchie fuer einen Online-Shop: ShopException als Basis, ProduktException, ZahlungsException, LieferException.
Uebung 3: Validierungs-Framework
Erstelle eine ValidationException, die eine Liste von Fehlern speichert:
throw new ValidationException(List.of(
"Name darf nicht leer sein",
"Alter muss positiv sein"
));
Uebung 4: Bank-System
Erstelle Exceptions fuer ein Banksystem: KontoNichtGefundenException, KontostandZuNiedrigException, UeberweisungsLimitException. Verwende sie in einer einfachen Bank-Klasse.
Was kommt als Naechstes?
Herzlichen Glueckwunsch — du hast alle theoretischen Grundlagen von Java gelernt! Jetzt kommt der spannendste Teil: Praxis-Projekte! Im naechsten Kapitel baust du einen Taschenrechner und wendest alles an, was du gelernt hast.
Zusammenfassung
- Eigene Exceptions erstellen durch Erweitern von
ExceptionoderRuntimeException - Unchecked (extends RuntimeException): Fangen optional, fuer Programmierfehler
- Checked (extends Exception): Fangen Pflicht, fuer aeussere Faktoren
- Eigene Exceptions koennen zusaetzliche Daten speichern (Kontostand, ID, etc.)
- Exception-Hierarchien ermoglichen gezieltes und allgemeines Fangen
- Bewahre die Ursache mit dem
cause-Parameter (Exception Chaining) - Verwende aussagekraeftige Fehlermeldungen mit konkreten Werten
Pro-Tipp: In der Praxis reichen oft die eingebauten Exceptions wie IllegalArgumentException und IllegalStateException voellig aus. Erstelle eigene Exceptions erst, wenn du zusaetzliche Daten mitgeben willst oder eine klare Hierarchie brauchst. Faustregel: Fuer Libraries und Frameworks lohnen sich eigene Exceptions, fuer kleine Projekte reichen die Standard-Exceptions.