Von Nova Trent, Junior Entwicklerin bei Java Fleet Systems Consulting
Schwierigkeit: 🔴 KOPFNUSS
Lesezeit: 40–45 Minuten
Voraussetzungen: Tag 1–9 abgeschlossen
📋 Kursübersicht: Java OOP in 10 Tagen
| Tag | Thema | Status |
|---|---|---|
| 1 | OOP-Konzepte & erste Klasse | ✅ Abgeschlossen |
| 2 | Attribute & Methoden | ✅ Abgeschlossen |
| 3 | Datenkapselung & Sichtbarkeit | ✅ Abgeschlossen |
| 4 | Konstruktoren | ✅ Abgeschlossen |
| 5 | Konstanten & Static | ✅ Abgeschlossen 🔴 |
| 6 | Vererbung – Grundlagen | ✅ Abgeschlossen |
| 7 | Vererbung – Polymorphie & Abstrakte Klassen | ✅ Abgeschlossen |
| 8 | Typumwandlung & instanceof | ✅ Abgeschlossen |
| 9 | Interfaces & Enumerationen | ✅ Abgeschlossen |
| → 10 | Ausnahmebehandlung | 📍 FINALE! 🔴 |
Voraussetzung: Tag 1–9 abgeschlossen
🔄 Kurz-Wiederholung: Challenge von Tag 9
Die Aufgabe war: Erstelle ein Zahlungssystem mit Interface und Enum.
Lösung:
public interface Zahlungsmittel {
boolean bezahlen(double betrag);
double getGuthaben();
}
public class Kreditkarte implements Zahlungsmittel {
private double kreditlimit;
private double genutzt = 0;
public Kreditkarte(double kreditlimit) {
this.kreditlimit = kreditlimit;
}
@Override
public boolean bezahlen(double betrag) {
if (genutzt + betrag > kreditlimit) {
System.out.println("❌ Kreditlimit überschritten!");
return false;
}
genutzt += betrag;
System.out.println("✅ Zahlung: " + betrag + "€ (Limit: " +
(kreditlimit - genutzt) + "€ frei)");
return true;
}
@Override
public double getGuthaben() {
return kreditlimit - genutzt;
}
}
public class PayPal implements Zahlungsmittel {
private String email;
private double guthaben;
public PayPal(String email, double guthaben) {
this.email = email;
this.guthaben = guthaben;
}
@Override
public boolean bezahlen(double betrag) {
if (betrag > guthaben) {
System.out.println("❌ Nicht genug Guthaben!");
return false;
}
guthaben -= betrag;
System.out.println("✅ PayPal-Zahlung: " + betrag + "€");
return true;
}
@Override
public double getGuthaben() { return guthaben; }
}
public enum Waehrung {
EUR("€"), USD("$"), GBP("£"), CHF("CHF");
private final String symbol;
Waehrung(String symbol) { this.symbol = symbol; }
public String getSymbol() { return symbol; }
}
Du bist bereit für die finale KOPFNUSS! 💪
⚡ Das Wichtigste in 30 Sekunden
Das Problem: Fehler passieren. Datei nicht gefunden, Division durch Null, Server nicht erreichbar. Ohne Behandlung stürzt dein Programm ab.
Die Lösung: Exceptions! Java’s Mechanismus für kontrollierte Fehlerbehandlung.
Heute lernst du:
- ✅
try-catch-finally— Fehler abfangen und behandeln - ✅ Checked vs. Unchecked Exceptions
- ✅
throwvs.throws— Unterschied verstehen - ✅ Eigene Exceptions erstellen
- ✅ Best Practices für Exception-Handling
Zeit-Investment: 40–45 Minuten
👋 Nova: „Exceptions haben mir den Job gerettet!“
Hey! 👋
Nova hier.
Real talk: Mein erstes Produktionsproblem war eine NullPointerException um 3 Uhr nachts. Der Server crashte, Kunden konnten nicht bestellen, mein Handy klingelte.
Seit dem Tag behandle ich Exceptions IMMER ordentlich. Nicht weil’s Spaß macht, sondern weil ich gelernt habe: Unbehandelte Exceptions = Schlaflose Nächte.
Los geht’s — das ist wichtig! 🚀
🟢 GRUNDLAGEN
Was ist eine Exception?
Eine Exception (Ausnahme) ist ein Ereignis, das den normalen Programmablauf unterbricht.
int[] zahlen = {1, 2, 3};
System.out.println(zahlen[99]); // 💥 ArrayIndexOutOfBoundsException!
Ohne Behandlung: Programm stürzt ab!
try-catch — Fehler abfangen
try {
// Code, der fehlschlagen könnte
int[] zahlen = {1, 2, 3};
System.out.println(zahlen[99]);
} catch (ArrayIndexOutOfBoundsException e) {
// Was tun, wenn der Fehler auftritt
System.out.println("Index existiert nicht!");
}
System.out.println("Programm läuft weiter!"); // ✅ Wird ausgeführt!
try-catch-finally
finally wird IMMER ausgeführt — ob Fehler oder nicht!
FileReader reader = null;
try {
reader = new FileReader("datei.txt");
// Datei lesen...
} catch (FileNotFoundException e) {
System.out.println("Datei nicht gefunden!");
} finally {
// Aufräumen — IMMER!
if (reader != null) {
try { reader.close(); } catch (IOException e) { }
}
System.out.println("Finally wird immer ausgeführt!");
}

Abbildung 1: Der Ablauf von try-catch-finally — finally läuft IMMER!
Mehrere catch-Blöcke
Du kannst verschiedene Exceptions unterschiedlich behandeln:
try {
int zahl = Integer.parseInt(input);
int ergebnis = 100 / zahl;
} catch (NumberFormatException e) {
System.out.println("Keine gültige Zahl!");
} catch (ArithmeticException e) {
System.out.println("Division durch Null!");
} catch (Exception e) {
// Fängt alles andere
System.out.println("Unbekannter Fehler: " + e.getMessage());
}
Wichtig: Spezifische Exceptions zuerst, allgemeine zuletzt!
Multi-Catch (Java 7+)
try {
// ...
} catch (NumberFormatException | ArithmeticException e) {
System.out.println("Eingabefehler: " + e.getMessage());
}
Exception-Informationen
Jede Exception hat nützliche Methoden:
try {
int x = 1 / 0;
} catch (ArithmeticException e) {
System.out.println("Message: " + e.getMessage());
System.out.println("Klasse: " + e.getClass().getName());
e.printStackTrace(); // Kompletter Stack-Trace
}
Checked vs. Unchecked Exceptions

Abbildung 2: Checked Exceptions MÜSSEN behandelt werden, Unchecked nicht.
Checked Exceptions:
- Erben von
Exception(aber nicht vonRuntimeException) - MÜSSEN behandelt werden (try-catch ODER throws)
- Beispiele:
IOException,SQLException,FileNotFoundException
Unchecked Exceptions:
- Erben von
RuntimeException - Müssen NICHT behandelt werden (aber können)
- Beispiele:
NullPointerException,IllegalArgumentException
// Checked — Compiler zwingt zur Behandlung:
public void lesenDatei() throws IOException {
Files.readString(Path.of("test.txt"));
}
// Unchecked — Keine Behandlung nötig:
public void berechne(int x) {
if (x < 0) {
throw new IllegalArgumentException("x muss positiv sein!");
}
}
throw — Exception werfen
Mit throw wirfst du selbst eine Exception:
public void setAlter(int alter) {
if (alter < 0) {
throw new IllegalArgumentException("Alter darf nicht negativ sein!");
}
if (alter > 150) {
throw new IllegalArgumentException("Unrealistisches Alter!");
}
this.alter = alter;
}
throws — Exception deklarieren
Mit throws sagst du: „Diese Methode KANN diese Exception werfen.“
public String lesenDatei(String pfad) throws IOException {
return Files.readString(Path.of(pfad));
}
Der Aufrufer muss dann entscheiden:
- Selbst behandeln (try-catch)
- Weitergeben (throws)
Abbildung 3: throw wirft, throws deklariert!
try-with-resources (Java 7+)
Für Ressourcen, die geschlossen werden müssen (Dateien, Verbindungen):
// ALT: Manuelles Schließen in finally
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("test.txt"));
// ...
} finally {
if (reader != null) reader.close();
}
// NEU: try-with-resources — schließt automatisch!
try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))) {
String zeile = reader.readLine();
System.out.println(zeile);
} // reader wird automatisch geschlossen!
Funktioniert mit allem, was AutoCloseable implementiert.
🟡 PROFESSIONALS
Schon Exception-Erfahrung aus C#, C++, PHP? Hier sind die Java-Besonderheiten.
Für C#-Umsteiger
// C# — Keine Checked Exceptions!
public void ReadFile(string path) {
File.ReadAllText(path); // Keine throws-Deklaration nötig
}
// Java — Checked Exceptions!
public void lesenDatei(String pfad) throws IOException {
Files.readString(Path.of(pfad)); // IOException ist checked!
}
Für C++-Umsteiger
// C++ — Exception-Spezifikation (deprecated) void foo() throw(std::runtime_error);
// Java — throws ist Standard void foo() throws RuntimeException;
Exception-Chaining
try {
// Original-Exception
} catch (SQLException e) {
// Neue Exception mit Original als Ursache
throw new DataAccessException("Datenbankfehler", e);
}
Eigene Exceptions erstellen
// Checked Exception
public class KontoException extends Exception {
public KontoException(String message) {
super(message);
}
public KontoException(String message, Throwable cause) {
super(message, cause);
}
}
// Unchecked Exception
public class UngueltigerBetragException extends RuntimeException {
private final double betrag;
public UngueltigerBetragException(double betrag) {
super("Ungültiger Betrag: " + betrag);
this.betrag = betrag;
}
public double getBetrag() { return betrag; }
}
🔵 BONUS
Für Wissbegierige: Best Practices, Anti-Patterns, Logging.
Best Practices
1. Fange nur, was du behandeln kannst:
// ❌ SCHLECHT: Leerer catch-Block
try {
riskanterCode();
} catch (Exception e) {
// Nichts tun — BÖSE!
}
// ✅ GUT: Sinnvoll behandeln
try {
riskanterCode();
} catch (IOException e) {
logger.error("Fehler beim Lesen", e);
throw new ServiceException("Daten nicht verfügbar", e);
}
2. Spezifische Exceptions fangen:
// ❌ SCHLECHT: Zu allgemein catch (Exception e) // ✅ GUT: Spezifisch catch (FileNotFoundException e)
3. Exceptions nicht für Kontrollfluss:
// ❌ SCHLECHT: Exception als if-else
try {
return liste.get(index);
} catch (IndexOutOfBoundsException e) {
return defaultWert;
}
// ✅ GUT: Prüfen vor Zugriff
if (index < liste.size()) {
return liste.get(index);
} else {
return defaultWert;
}
4. Ressourcen immer schließen:
// ✅ try-with-resources
try (Connection conn = dataSource.getConnection()) {
// ...
}
Anti-Patterns
Pokemon Exception Handling:
// ❌ "Gotta catch 'em all!"
try {
// ...
} catch (Exception e) {
// Ignorieren
}
Log and Throw:
// ❌ Doppelt gemoppelt
catch (Exception e) {
logger.error("Fehler", e);
throw e; // Wird nochmal geloggt!
}
Logging statt printStackTrace()
// ❌ SCHLECHT: In Produktion
e.printStackTrace();
// ✅ GUT: Mit Logger
private static final Logger logger = LoggerFactory.getLogger(MyClass.class);
catch (Exception e) {
logger.error("Fehler bei Verarbeitung", e);
}
💬 Real Talk
Tom fragt, Nova erklärt — Die Fragen, die sich jeder stellt.
Java Fleet Serverraum, 22:00 Uhr. (Warum auch nicht.)
Tom: Nova, checked vs. unchecked — ich check’s nicht. Wann was?
Nova: Einfache Regel: Checked für Dinge außerhalb deiner Kontrolle (Datei, Netzwerk, Datenbank). Unchecked für Programmierfehler (null, falscher Index).
Tom: Und warum muss ich checked behandeln?
Nova: Weil der Compiler dich zwingt, über Fehler nachzudenken. Eine Datei KANN nicht existieren. Das ist kein Bug, das ist Realität.
Tom: Okay… und wenn ich einfach throws Exception überall hinschreibe?
Nova: zuckt zusammen Dann hasst dich jeder, der deinen Code benutzt. Sei spezifisch! throws IOException sagt was. throws Exception sagt nichts.
Tom: Was ist mit finally? Wann brauch ich das?
Nova: Für Aufräumarbeiten. Aber ehrlich? Seit Java 7 nutze ich try-with-resources. Das macht’s automatisch.
Tom: Eine letzte Frage: Leere catch-Blöcke?
Nova: starrt Nein. Niemals. NIEMALS. Das ist wie Rauchmelder abschalten, weil sie nerven.
✅ Checkpoint: Hast du es verstanden?
Quiz
Frage 1: Was ist der Unterschied zwischen throw und throws?
Frage 2: Wird der finally-Block ausgeführt, wenn im try-Block ein return steht?
Frage 3: Ist NullPointerException checked oder unchecked?
Frage 4: Dieser Code:
try {
throw new IOException("Fehler");
} catch (Exception e) {
System.out.println("A");
} catch (IOException e) {
System.out.println("B");
}
Kompiliert das? Warum (nicht)?
Frage 5: Was ist das Problem mit diesem Code?
try {
riskanteOperation();
} catch (Exception e) {
// TODO: Später behandeln
}
Abschluss-Challenge: Bankkonto mit Exceptions
Aufgabe: Erstelle ein robustes Bankkonto-System:
- Eigene Exception
KontoException(checked):- Mit Nachricht und optionaler Ursache
- Eigene Exception
UngueltigerBetragException(unchecked):- Speichert den ungültigen Betrag
- Klasse
Bankkonto:- Attribute:
kontonummer,inhaber,kontostand - Methode
einzahlen(double betrag)— wirftUngueltigerBetragExceptionbei negativem Betrag - Methode
abheben(double betrag)— wirftKontoExceptionbei zu wenig Guthaben - Methode
ueberweisen(Bankkonto ziel, double betrag)— kombiniert beide
- Attribute:
- Main-Methode:
- Teste alle Fehlerfälle
- Zeige saubere Fehlerbehandlung
Das ist deine Abschlussarbeit! 🎓
❓ FAQ — Häufig gestellte Fragen
Frage 1: Soll ich RuntimeException oder Exception erweitern?
RuntimeException für Programmierfehler (sollten durch besseren Code vermieden werden). Exception für erwartbare Fehler (Datei nicht gefunden, Netzwerkfehler).
Frage 2: Was passiert bei Exception im finally-Block?
Die ursprüngliche Exception geht verloren! Sehr gefährlich. Vermeide Exceptions in finally.
Frage 3: Kann ich mehrere Exceptions in throws deklarieren?
Ja! public void foo() throws IOException, SQLException
Frage 4: Was ist ein Stack-Trace?
Die Aufrufkette bis zum Fehler. Zeigt genau, wo das Problem ist. Immer lesen, von unten nach oben!
Frage 5: Warum gibt es checked Exceptions?
Java’s Design-Entscheidung: Zwingt Entwickler, über Fehler nachzudenken. Kontrovers, aber hat Gründe.
Frage 6: Wie behandle ich Exceptions in Streams/Lambdas?
Kompliziert! Checked Exceptions in Lambdas sind umständlich. Entweder wrappen oder Sneaky Throws (fortgeschritten).
Frage 7: Bernd sagt, er fängt nie Exceptions. Wie überlebt er?
lacht Bernd übertreibt. Er meint: Er fängt sie an der RICHTIGEN Stelle — nicht überall, sondern strategisch. In Service-Methoden, an API-Grenzen. Nicht in jeder Zeile.
Bernd hat auch 40 Jahre Erfahrung. Er weiß, wo Fehler auftreten können. Du und ich? Wir brauchen die Compiler-Hilfe noch. 😄
📚 Quiz-Lösungen
Frage 1: throw vs. throws?
Antwort:
throw: Wirft eine Exception (im Methodenkörper)throws: Deklariert, dass eine Methode eine Exception werfen KANN (in der Signatur)
public void foo() throws IOException { // throws = Deklaration
throw new IOException("Fehler"); // throw = Werfen
}
Frage 2: finally bei return?
Antwort:
Ja! finally wird IMMER ausgeführt, auch bei return, break, oder Exception.
try {
return "A";
} finally {
System.out.println("Läuft!"); // Wird ausgegeben!
}
Frage 3: NullPointerException checked oder unchecked?
Antwort:
Unchecked! Es erbt von RuntimeException. Muss nicht behandelt werden (sollte durch null-Checks vermieden werden).
Frage 4: Kompiliert das?
} catch (Exception e) {
System.out.println("A");
} catch (IOException e) { // ❌ Unerreichbar!
System.out.println("B");
}
Antwort:
Nein! Compilerfehler: „exception IOException has already been caught“
IOException ist Subtyp von Exception. Der erste catch fängt alles, der zweite ist unerreichbar.
Lösung: Spezifische zuerst!
Frage 5: Problem mit leerem catch?
Antwort:
Der Fehler wird verschluckt. Das Programm läuft weiter, aber du weißt nicht, dass etwas schiefging. Debugging-Albtraum!
Mindestens loggen:
catch (Exception e) {
logger.error("Fehler", e);
}
🎉 KURS ABGESCHLOSSEN! 🎉
DU HAST ES GESCHAFFT! 🚀🎓
In 10 Tagen hast du gelernt:
| Tag | Thema | ✅ |
|---|---|---|
| 1 | OOP-Konzepte & erste Klasse | ✅ |
| 2 | Attribute & Methoden | ✅ |
| 3 | Datenkapselung & Sichtbarkeit | ✅ |
| 4 | Konstruktoren | ✅ |
| 5 | Konstanten & Static 🔴 | ✅ |
| 6 | Vererbung – Grundlagen | ✅ |
| 7 | Polymorphie & Abstrakte Klassen | ✅ |
| 8 | Typumwandlung & instanceof | ✅ |
| 9 | Interfaces & Enumerationen | ✅ |
| 10 | Ausnahmebehandlung 🔴 | ✅ |
Du beherrschst jetzt:
- Klassen, Objekte, Konstruktoren
- Kapselung und Sichtbarkeit
- Vererbung und Polymorphie
- Abstrakte Klassen und Interfaces
- Enums und Typumwandlung
- Exception-Handling
Das ist die Grundlage für ALLES in Java! 💪
🔮 Wie geht’s weiter?
Empfohlene nächste Schritte:
- Collections Framework — Listen, Maps, Sets
- Streams & Lambdas — Funktionale Programmierung
- Generics — Typsichere Collections
- Multithreading — Parallele Programmierung
- Spring Boot — Enterprise-Entwicklung
Schau auf java-developer.online für weitere Kurse! 🎯
📦 Downloads
Starter-Projekt für Tag 10:
⬇️ Tag10_Ausnahmebehandlung.zip — Komplettes Maven-Projekt
Inhalt:
Tag10_Ausnahmebehandlung/
├── pom.xml
├── README.md
└── src/main/java/
└── de/javafleet/oop/
├── Main.java
├── model/
│ ├── Bankkonto.java
│ ├── Zahlungsmittel.java (Lösung Tag 9)
│ ├── Kreditkarte.java
│ └── PayPal.java
├── exceptions/
│ ├── KontoException.java
│ └── UngueltigerBetragException.java
└── enums/
└── Waehrung.java
Grafiken:
- ⬇️ try-catch-finally.svg — Ablauf visualisiert
- ⬇️ checked-vs-unchecked.svg — Exception-Hierarchie
- ⬇️ throw-vs-throws.svg — Der Unterschied
🔧 Troubleshooting
Problem: „unreported exception; must be caught or declared“
error: unreported exception IOException; must be caught or declared to be thrown
Lösung: Checked Exception! Entweder try-catch oder throws hinzufügen.
Problem: „exception has already been caught“
error: exception IOException has already been caught
Lösung: Spezifische Exceptions VOR allgemeine!
Problem: NullPointerException zur Laufzeit
Lösung: Null-Checks! Oder Optional verwenden (fortgeschritten).
🔗 Resources & Links
🟢 Für Einsteiger
| Ressource | Beschreibung | Sprache |
|---|---|---|
| Oracle — Exceptions | Offizielle Doku | 🇬🇧 |
| Baeldung — Exception Handling | Praktische Beispiele | 🇬🇧 |
🟡 Für Fortgeschrittene
| Ressource | Beschreibung | Sprache |
|---|---|---|
| Effective Java — Item 69-77 | Exception Best Practices | 🇬🇧 |
👋 Bis zum nächsten Kurs!
Du hast den OOP-Kurs gemeistert! 🎓
Ich bin stolz auf dich. Wirklich. OOP ist nicht einfach, und du hast durchgehalten.
Jetzt raus in die Praxis! Bau was. Mach Fehler. Lern daraus. Das ist der einzige Weg.
Wir sehen uns! 🚀
Nova Trent
Junior Entwicklerin bei Java Fleet Systems Consulting
„Exceptions sind nicht der Feind — ignorierte Exceptions sind es!“ 💎
Praxis-Challenge 🔴 KOPFNUSS
🎯 Bestellsystem mit Fehlerbehandlung
Schwierigkeit: 🔴 Fortgeschritten (KOPFNUSS)
Geschätzte Zeit: 90–120 Minuten
Voraussetzungen: Tag 1–10 abgeschlossen
Szenario
Du entwickelst ein Bestellsystem für einen Online-Shop. In der echten Welt geht ständig etwas schief: Produkte sind nicht auf Lager, Zahlungen schlagen fehl, Adressen sind ungültig. Dein System muss mit all diesen Fehlern robust umgehen.
Diese Aufgabe testet alle Aspekte der Ausnahmebehandlung:
- Eigene Exceptions (checked & unchecked)
- try-catch-finally
- Multi-catch
- throws deklarieren
- Exception Chaining
- try-with-resources
- Best Practices
Teil 1: Eigene Exception-Klassen
1.1 BestellException (Checked Exception)
Die Basis-Exception für alle Bestellfehler.
public class BestellException extends Exception {
// Konstruktoren für: message, message + cause
}
1.2 ProduktNichtVerfuegbarException extends BestellException
Wird geworfen, wenn ein Produkt nicht auf Lager ist.
| Element | Details |
|---|---|
| Zusätzliche Attribute | produktId (int), angefordert (int), verfuegbar (int) |
| Konstruktor | Alle Parameter + generiert passende Message |
| Getter | Für alle Attribute |
Generierte Message: "Produkt #123: 5 angefordert, aber nur 2 verfügbar"
1.3 ZahlungFehlgeschlagenException extends BestellException
Wird geworfen, wenn eine Zahlung abgelehnt wird.
| Element | Details |
|---|---|
| Zusätzliche Attribute | grund (String), betrag (double) |
| Konstruktor | Alle Parameter |
| Getter | Für alle Attribute |
1.4 UngueltigeAdresseException extends BestellException
Wird geworfen, wenn die Lieferadresse ungültig ist.
| Element | Details |
|---|---|
| Zusätzliche Attribute | feld (String, z.B. „PLZ“, „Straße“) |
| Konstruktor | Parameter: feld, message |
| Getter | Für feld |
1.5 SystemFehlerException (Unchecked Exception)
Für unerwartete technische Fehler (Datenbank, Netzwerk…).
public class SystemFehlerException extends RuntimeException {
// Konstruktoren für: message, message + cause
}
Teil 2: Domänenklassen
2.1 Klasse Produkt
| Element | Details |
|---|---|
| Attribute | id (int), name (String), preis (double), lagerbestand (int) |
| Konstruktor | Alle Parameter |
| Methode | void reduziereBestand(int menge) throws ProduktNichtVerfuegbarException |
| Getter/Setter | Für alle Attribute |
reduziereBestand():
- Wirft
ProduktNichtVerfuegbarExceptionwennmenge > lagerbestand - Sonst: reduziert Bestand
2.2 Klasse Adresse
| Element | Details |
|---|---|
| Attribute | strasse (String), hausnummer (String), plz (String), ort (String) |
| Konstruktor | Alle Parameter |
| Methode | void validieren() throws UngueltigeAdresseException |
| Getter | Für alle Attribute |
validieren():
- Wirft Exception wenn
strasseleer oder null → Feld: „Straße“ - Wirft Exception wenn
plznicht genau 5 Ziffern → Feld: „PLZ“ - Wirft Exception wenn
ortleer oder null → Feld: „Ort“
2.3 Klasse BestellPosition
| Element | Details |
|---|---|
| Attribute | produkt (Produkt), menge (int) |
| Konstruktor | Beide Parameter |
| Methode | double getGesamtpreis() |
| Getter | Für beide Attribute |
2.4 Klasse Bestellung
| Element | Details |
|---|---|
| Attribute | bestellNummer (String), positionen (List<BestellPosition>), lieferadresse (Adresse), status (String) |
| Konstruktor | Parameter: bestellNummer, lieferadresse. Liste startet leer, Status = „NEU“ |
| Methode | void addPosition(BestellPosition pos) |
| Methode | double getGesamtsumme() |
| Getter | Für alle Attribute |
| Setter | Für status |
Teil 3: Service-Klassen
3.1 Klasse ZahlungsService
Simuliert die Zahlungsabwicklung.
public class ZahlungsService {
/**
* Verarbeitet eine Zahlung.
* @param betrag Der zu zahlende Betrag
* @param zahlungsart "KREDITKARTE", "PAYPAL", "RECHNUNG"
* @throws ZahlungFehlgeschlagenException wenn Zahlung abgelehnt
*/
public void verarbeiteZahlung(double betrag, String zahlungsart)
throws ZahlungFehlgeschlagenException {
// Simulierte Regeln:
// - KREDITKARTE: Abgelehnt wenn betrag > 1000
// - PAYPAL: Immer erfolgreich
// - RECHNUNG: Abgelehnt wenn betrag > 500
// - Unbekannte Zahlungsart: Abgelehnt
}
}
3.2 Klasse BestellLogger
Implementiert AutoCloseable für try-with-resources!
public class BestellLogger implements AutoCloseable {
private String logDatei;
private boolean istOffen;
public BestellLogger(String logDatei) {
this.logDatei = logDatei;
this.istOffen = true;
System.out.println("[LOG] Logger geöffnet: " + logDatei);
}
public void log(String nachricht) {
if (!istOffen) {
throw new IllegalStateException("Logger ist bereits geschlossen!");
}
System.out.println("[LOG] " + nachricht);
}
@Override
public void close() {
// Gibt Meldung aus und setzt istOffen = false
}
}
3.3 Klasse BestellService
Die Hauptklasse — hier kommt alles zusammen!
public class BestellService {
private ZahlungsService zahlungsService = new ZahlungsService();
/**
* Verarbeitet eine komplette Bestellung.
*
* Diese Methode demonstriert:
* - try-with-resources (Logger)
* - Multi-catch
* - Exception chaining
* - finally-Block
*
* @param bestellung Die zu verarbeitende Bestellung
* @param zahlungsart Die gewählte Zahlungsart
* @throws BestellException wenn die Bestellung nicht verarbeitet werden kann
*/
public void verarbeiteBestellung(Bestellung bestellung, String zahlungsart)
throws BestellException {
// 1. try-with-resources für Logger
// 2. Adresse validieren
// 3. Lagerbestand für alle Positionen prüfen und reduzieren
// 4. Zahlung durchführen
// 5. Status setzen
// 6. Bei Fehlern: Exception mit Kontext werfen (Chaining)
// 7. finally: Immer "Verarbeitung beendet" loggen (außerhalb try-with-resources)
}
/**
* Verarbeitet mehrere Bestellungen und sammelt Fehler.
* Stoppt NICHT bei der ersten fehlerhaften Bestellung!
*
* @param bestellungen Liste der Bestellungen
* @param zahlungsart Die Zahlungsart für alle
* @return Map mit BestellNummer → Ergebnis ("OK" oder Fehlermeldung)
*/
public Map<String, String> verarbeiteAlle(List<Bestellung> bestellungen, String zahlungsart) {
// Verarbeitet jede Bestellung einzeln
// Fängt Exceptions und speichert sie in der Map
// Gibt am Ende die Map zurück
}
}
Teil 4: Implementierungs-Details
verarbeiteBestellung() — Schritt für Schritt
public void verarbeiteBestellung(Bestellung bestellung, String zahlungsart)
throws BestellException {
System.out.println("=== Verarbeite Bestellung " + bestellung.getBestellNummer() + " ===");
try (BestellLogger logger = new BestellLogger("bestellung_" + bestellung.getBestellNummer() + ".log")) {
// 1. Adresse validieren
logger.log("Prüfe Lieferadresse...");
bestellung.getLieferadresse().validieren();
logger.log("Adresse OK");
// 2. Lagerbestand prüfen und reduzieren
logger.log("Prüfe Lagerbestand...");
for (BestellPosition pos : bestellung.getPositionen()) {
pos.getProdukt().reduziereBestand(pos.getMenge());
logger.log("Produkt " + pos.getProdukt().getName() + ": " + pos.getMenge() + " reserviert");
}
// 3. Zahlung durchführen
logger.log("Führe Zahlung durch...");
double summe = bestellung.getGesamtsumme();
zahlungsService.verarbeiteZahlung(summe, zahlungsart);
logger.log("Zahlung erfolgreich: " + summe + " EUR");
// 4. Status setzen
bestellung.setStatus("ABGESCHLOSSEN");
logger.log("Bestellung abgeschlossen!");
} catch (UngueltigeAdresseException e) {
// Exception Chaining: Ursprüngliche Exception als cause
bestellung.setStatus("FEHLER_ADRESSE");
throw new BestellException("Bestellung " + bestellung.getBestellNummer() +
" fehlgeschlagen: Ungültige Adresse (" + e.getFeld() + ")", e);
} catch (ProduktNichtVerfuegbarException e) {
bestellung.setStatus("FEHLER_LAGER");
throw new BestellException("Bestellung " + bestellung.getBestellNummer() +
" fehlgeschlagen: " + e.getMessage(), e);
} catch (ZahlungFehlgeschlagenException e) {
bestellung.setStatus("FEHLER_ZAHLUNG");
throw new BestellException("Bestellung " + bestellung.getBestellNummer() +
" fehlgeschlagen: Zahlung abgelehnt (" + e.getGrund() + ")", e);
} finally {
System.out.println("=== Verarbeitung " + bestellung.getBestellNummer() + " beendet ===\n");
}
}
Test-Code für Main
public class Main {
public static void main(String[] args) {
BestellService service = new BestellService();
// === Produkte anlegen ===
Produkt laptop = new Produkt(1, "Laptop", 999.99, 5);
Produkt maus = new Produkt(2, "Maus", 29.99, 100);
Produkt tastatur = new Produkt(3, "Tastatur", 79.99, 3);
Produkt monitor = new Produkt(4, "Monitor", 399.99, 0); // Nicht auf Lager!
// === Test 1: Erfolgreiche Bestellung ===
System.out.println("########## TEST 1: Erfolgreiche Bestellung ##########\n");
try {
Adresse adresse1 = new Adresse("Hauptstraße", "42", "12345", "Berlin");
Bestellung bestellung1 = new Bestellung("B-001", adresse1);
bestellung1.addPosition(new BestellPosition(maus, 2));
bestellung1.addPosition(new BestellPosition(tastatur, 1));
service.verarbeiteBestellung(bestellung1, "PAYPAL");
System.out.println("Status: " + bestellung1.getStatus());
} catch (BestellException e) {
System.out.println("FEHLER: " + e.getMessage());
}
// === Test 2: Ungültige Adresse ===
System.out.println("\n########## TEST 2: Ungültige Adresse ##########\n");
try {
Adresse adresse2 = new Adresse("", "1", "123", "Hamburg"); // Straße leer, PLZ zu kurz
Bestellung bestellung2 = new Bestellung("B-002", adresse2);
bestellung2.addPosition(new BestellPosition(maus, 1));
service.verarbeiteBestellung(bestellung2, "PAYPAL");
} catch (BestellException e) {
System.out.println("FEHLER: " + e.getMessage());
if (e.getCause() != null) {
System.out.println("Ursache: " + e.getCause().getClass().getSimpleName());
}
}
// === Test 3: Produkt nicht auf Lager ===
System.out.println("\n########## TEST 3: Produkt nicht auf Lager ##########\n");
try {
Adresse adresse3 = new Adresse("Nebenstraße", "7", "54321", "München");
Bestellung bestellung3 = new Bestellung("B-003", adresse3);
bestellung3.addPosition(new BestellPosition(monitor, 1)); // Lagerbestand = 0!
service.verarbeiteBestellung(bestellung3, "PAYPAL");
} catch (BestellException e) {
System.out.println("FEHLER: " + e.getMessage());
}
// === Test 4: Zahlung abgelehnt ===
System.out.println("\n########## TEST 4: Zahlung abgelehnt ##########\n");
try {
Adresse adresse4 = new Adresse("Parkweg", "99", "11111", "Köln");
Bestellung bestellung4 = new Bestellung("B-004", adresse4);
bestellung4.addPosition(new BestellPosition(laptop, 2)); // 2x 999.99 = 1999.98 > 1000!
service.verarbeiteBestellung(bestellung4, "KREDITKARTE"); // Limit 1000!
} catch (BestellException e) {
System.out.println("FEHLER: " + e.getMessage());
}
// === Test 5: Mehrere Bestellungen auf einmal ===
System.out.println("\n########## TEST 5: Batch-Verarbeitung ##########\n");
List<Bestellung> batch = new ArrayList<>();
// Gültige Bestellung
Adresse a1 = new Adresse("Straße A", "1", "11111", "Stadt A");
Bestellung b1 = new Bestellung("BATCH-001", a1);
b1.addPosition(new BestellPosition(maus, 1));
batch.add(b1);
// Ungültige PLZ
Adresse a2 = new Adresse("Straße B", "2", "ABC", "Stadt B");
Bestellung b2 = new Bestellung("BATCH-002", a2);
b2.addPosition(new BestellPosition(maus, 1));
batch.add(b2);
// Gültige Bestellung
Adresse a3 = new Adresse("Straße C", "3", "33333", "Stadt C");
Bestellung b3 = new Bestellung("BATCH-003", a3);
b3.addPosition(new BestellPosition(tastatur, 1));
batch.add(b3);
Map<String, String> ergebnisse = service.verarbeiteAlle(batch, "PAYPAL");
System.out.println("=== Batch-Ergebnisse ===");
for (Map.Entry<String, String> entry : ergebnisse.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// === Test 6: Multi-catch Demo ===
System.out.println("\n########## TEST 6: Multi-catch Demo ##########\n");
demonstriereMultiCatch();
}
/**
* Demonstriert Multi-catch (Java 7+).
*/
private static void demonstriereMultiCatch() {
try {
// Simuliere verschiedene mögliche Fehler
int zufallsFehler = (int) (Math.random() * 3);
if (zufallsFehler == 0) {
throw new IllegalArgumentException("Ungültiges Argument");
} else if (zufallsFehler == 1) {
throw new IllegalStateException("Ungültiger Zustand");
} else {
System.out.println("Kein Fehler aufgetreten");
}
} catch (IllegalArgumentException | IllegalStateException e) {
// Multi-catch: Beide Exceptions gleich behandeln
System.out.println("Gefangen mit Multi-catch: " + e.getClass().getSimpleName());
System.out.println("Message: " + e.getMessage());
}
}
}
Erwartete Ausgabe (ungefähr)
########## TEST 1: Erfolgreiche Bestellung ########## === Verarbeite Bestellung B-001 === [LOG] Logger geöffnet: bestellung_B-001.log [LOG] Prüfe Lieferadresse... [LOG] Adresse OK [LOG] Prüfe Lagerbestand... [LOG] Produkt Maus: 2 reserviert [LOG] Produkt Tastatur: 1 reserviert [LOG] Führe Zahlung durch... [LOG] Zahlung erfolgreich: 139.97 EUR [LOG] Bestellung abgeschlossen! [LOG] Logger geschlossen: bestellung_B-001.log === Verarbeitung B-001 beendet === Status: ABGESCHLOSSEN ########## TEST 2: Ungültige Adresse ########## === Verarbeite Bestellung B-002 === [LOG] Logger geöffnet: bestellung_B-002.log [LOG] Prüfe Lieferadresse... [LOG] Logger geschlossen: bestellung_B-002.log === Verarbeitung B-002 beendet === FEHLER: Bestellung B-002 fehlgeschlagen: Ungültige Adresse (Straße) Ursache: UngueltigeAdresseException ########## TEST 3: Produkt nicht auf Lager ########## === Verarbeite Bestellung B-003 === [LOG] Logger geöffnet: bestellung_B-003.log [LOG] Prüfe Lieferadresse... [LOG] Adresse OK [LOG] Prüfe Lagerbestand... [LOG] Logger geschlossen: bestellung_B-003.log === Verarbeitung B-003 beendet === FEHLER: Bestellung B-003 fehlgeschlagen: Produkt #4: 1 angefordert, aber nur 0 verfügbar ########## TEST 4: Zahlung abgelehnt ########## === Verarbeite Bestellung B-004 === [LOG] Logger geöffnet: bestellung_B-004.log [LOG] Prüfe Lieferadresse... [LOG] Adresse OK [LOG] Prüfe Lagerbestand... [LOG] Produkt Laptop: 2 reserviert [LOG] Führe Zahlung durch... [LOG] Logger geschlossen: bestellung_B-004.log === Verarbeitung B-004 beendet === FEHLER: Bestellung B-004 fehlgeschlagen: Zahlung abgelehnt (Kreditkarten-Limit überschritten) ########## TEST 5: Batch-Verarbeitung ########## [... Logs für jede Bestellung ...] === Batch-Ergebnisse === BATCH-001: OK BATCH-002: Ungültige Adresse (PLZ) BATCH-003: OK ########## TEST 6: Multi-catch Demo ########## Gefangen mit Multi-catch: IllegalArgumentException Message: Ungültiges Argument
Checkliste: Das wird getestet
Eigene Exceptions
- [ ]
BestellException extends Exception(checked) - [ ]
SystemFehlerException extends RuntimeException(unchecked) - [ ] Spezialisierte Exceptions mit zusätzlichen Attributen
- [ ] Konstruktor mit
messageundcause
try-catch-finally
- [ ]
try-Block für gefährlichen Code - [ ] Mehrere
catch-Blöcke für verschiedene Exceptions - [ ]
finally-Block wird IMMER ausgeführt
try-with-resources
- [ ]
BestellLogger implements AutoCloseable - [ ]
close()wird automatisch aufgerufen - [ ] Funktioniert auch bei Exceptions
Multi-catch
- [ ]
catch (ExceptionA | ExceptionB e)Syntax - [ ] Gleiche Behandlung für mehrere Exception-Typen
Exception Chaining
- [ ]
throw new BestellException("...", originalException) - [ ]
getCause()gibt ursprüngliche Exception zurück
throws Deklaration
- [ ] Checked Exceptions in Signatur deklariert
- [ ] Unchecked Exceptions nicht deklariert
Hinweise
- Checked vs. Unchecked:
Exception→ Checked (muss behandelt oder deklariert werden)RuntimeException→ Unchecked (optional)
- try-with-resources: Der Logger wird automatisch geschlossen, auch bei Exceptions!
- Exception Chaining: Die
causeermöglicht Stack-Trace-Analyse - finally vs. close():
finallyläuft NACHclose()bei try-with-resources
Projektstruktur
src/main/java/
└── de/javafleet/oop/
├── Main.java
├── service/
│ ├── BestellService.java
│ ├── ZahlungsService.java
│ └── BestellLogger.java
├── model/
│ ├── Produkt.java
│ ├── Adresse.java
│ ├── BestellPosition.java
│ └── Bestellung.java
└── exception/
├── BestellException.java
├── ProduktNichtVerfuegbarException.java
├── ZahlungFehlgeschlagenException.java
├── UngueltigeAdresseException.java
└── SystemFehlerException.java
Bonus-Aufgaben
- Retry-Mechanismus: Implementiere automatische Wiederholung bei
ZahlungFehlgeschlagenException(max. 3 Versuche) - Rollback: Wenn die Zahlung fehlschlägt, mache die Lagerbestand-Reduktion rückgängig
- Exception-Report: Erstelle eine Methode, die alle Exceptions einer Batch-Verarbeitung als HTML-Report ausgibt
Lösung: Findest du in Tag10_Ausnahmebehandlung_Loesung.md
© 2025 Java Fleet Systems Consulting
Tags: #Java #OOP #Exceptions #TryCatch #Tutorial #Abschluss
© 2025 Java Fleet Systems Consulting | java-developer.online

