Java – Lernsession Zusammenfassung (verbessert)

1. Polymorphie

Zwei Typen, ein Objekt

  • Referenztyp (Compilertyp): Was du aufrufen darfst → Compiler entscheidet
  • Objekttyp (Laufzeittyp): Welche Methode tatsächlich ausgeführt wird → JVM entscheidet
Tier t = new Hund();  // Referenztyp: Tier | Objekttyp: Hund

Methoden vs. Attribute

MethodenAttribute
BindungDynamisch (JVM zur Laufzeit)Statisch (Compiler zur Kompilierzeit)
  • t.laut() → JVM schaut auf echten Typ (Hund) → “Wuff!” ✅
  • t.name → Compiler sieht nur Tier → “Tier” ❌ (nicht überschrieben!)
  • ((Hund)t).auge = 3; → Cast nötig, um Attribute der Unterklasse zu nutzen

⚠️ Wichtige Ergänzung: Overriding vs. Overloading

Overriding (Überschreiben)Overloading (Überladen)
Was?Gleicher Name + gleiche Parameter in UnterklassenGleicher Name, unterschiedliche Parameterliste — auch durch Vererbung möglich
BindungDynamisch → JVM entscheidet zur LaufzeitStatisch → Compiler entscheidet zur Kompilierzeit
BeispielHund.laut() überschreibt Tier.laut()add(int) und add(String) nebeneinander

💡 Merksatz: Überladene Methoden werden statisch gebunden. Überschriebene Methoden werden dynamisch gebunden.

⚠️ Typische Prüfungsfalle: Casting & instanceof

Wenn Referenztyp und Objekttyp auseinandergehen, musst du casten, um Unterklasse-spezifisches zu nutzen:

Tier t = new Hund();  // Referenztyp: Tier | Objekttyp: Hund
 
// ❌ Compiler-Error: t.bellen() geht nicht (Compiler sieht nur Tier)
// t.bellen();
 
// ✅ Erst prüfen, ob es ein Hund ist, dann casten
if (t instanceof Hund) {
    Hund h = (Hund) t;  // Cast nötig!
    h.bellen();         // Jetzt funktioniert's
}
 
// ⚠️ Ohne Prüfung: ClassCastException beim Cast zu Katze!
// Katze k = (Katze) t;  // 💥 Runtime-Error, weil t ein Hund ist!

💡 Merksatz: instanceof prüft den Objekttyp (Laufzeittyp), nicht den Referenztyp. Der Compiler entscheidet beim Cast nur, ob es möglicherweise funktioniert — die JVM führt es erst zur Laufzeit aus.

Warum Polymorphie?

Du schreibst Code für Tier und er funktioniert automatisch mit allen Unterklassen — auch neuen, die du später erstellst. (Open-Closed Principle)


2. Checked vs. Unchecked Exceptions

CheckedUnchecked
Müssen behandelt werden?✅ Ja (try-catch oder throws)❌ Nein
Wer wirft sie?Programmierer (throw new ...)JVM automatisch (bei Bugs) oder Programmierer bewusst
OberklasseExceptionRuntimeException
BeispieleIOException, eigene ExceptionsArithmeticException, NullPointerException, auch bewusst definierte RuntimeException-Unterklassen

Wichtige Korrektur zur Vereinfachung

“Checked = Fehler von außen, Unchecked = Fehler im Code” ❌

Diese Aussage ist eine Vereinfachung. Korrekter:

Checked ExceptionsUnchecked Exceptions
Was sind sie?Exceptions, die nicht von RuntimeException oder Error erben und vom Compiler behandelt oder deklariert werden müssen.Programmierfehler oder bewusst definierte Ausnahmen (z. B. eigene RuntimeException-Unterklassen).
Muss man sie behandeln?Ja (try-catch oder throws)Nein — aber: du kannst trotzdem fangen!

💡 Beispiel: Eine selbst definierte RuntimeException("Ungültige ID") ist technisch eine Unchecked Exception, wird aber bewusst vom Programmierer geworfen — sie ist kein Bug.


3. throw vs. throws

throwthrows
Wo?Im Code (innerhalb der Methode)Im Methodenkopf (Signatur)
Was?Wirft ein Exception-Objekt jetztKündigt an, dass Exceptions geworfen werden können
Analogie”Ich werfe jetzt eine Bombe!” 💣“In diesem Raum gibt es Bomben — pass auf!” ⚠️
// throws = ANKÜNDIGEN (Methodenkopf)
public void lesen() throws IOException { ... }
 
// throw = WERFEN (im Code)
if (DateiNichtVorhanden) throw new IOException("...");

4. Abstrakte Klassen & Interfaces

Abstrakte Klasse (abstract class)

  • Kann nicht instanziiert werden: new Form() ❌
  • Enthält abstrakte Methoden (ohne Körper), die Unterklassen implementieren müssen
  • Hat auch normale Attribute und Methoden
  • Darf konkrete und abstrakte Methoden mischen
abstract class Form {
    protected String name;
    
    public abstract double flaeche();  // muss überschrieben werden
    
    public void ausgeben() {           // kann auch normale Methoden haben
        System.out.println(name + ": " + flaeche());
    }
}

Interface (interface)

  • Alle Attribute sind automatisch public static final (Konstanten)
  • default-Methoden: können Implementierung haben, aber müssen nicht überschrieben werden
  • static-Methoden: gehören zur Schnittstelle selbst, nicht zu Instanzen
  • private-Hilfsmethoden: ab Java 9 möglich! Dienen nur der internen Umsetzung von default-Methoden

Wichtige Regeln (ergänzt)

RegelErklärung
Mehrfachvererbung bei InterfacesEine Klasse kann mehrere Interfaces implements: class C implements A, B {}
Interface-HierarchieEin Interface kann ein anderes Interface extends: interface X extends Y {}
Abstrakte MethodenInterfaces definieren Methoden ohne default → alle Implementierungen müssen sie überschreiben

5. Exception Chaining – Die Chain durchlaufen

Grundprinzip

Exceptions können verschachtelt werden — eine Exception “umhüllt” eine andere.

throw new Exception("Fehler beim Lesen", e);  // ← Chain!
// Parameter 1 = neue Nachricht | Parameter 2 = die umhüllte Exception (Cause)
MethodeGibt zurück
e.getMessage()Nur die Message dieser Ebene
e.getCause()Die direkt umhüllte Exception (nächste Ebene in der Chain)
e.getCause().getMessage()Message der direkt darunterliegenden Ebene

⚠️ Korrektur: getCause() gibt immer nur die direkte Ursache zurück

“Die nächste Ebene finden” ❌
getCause() gibt immer die unmittelbar umhüllte Exception zurück. Diese kann wiederum eine eigene Cause haben — man muss also rekursiv/traversieren, nicht einfach “die nächste”.

Beispiel: 3 Layer

// Layer 0: "test.txt (No such file or directory)"
// Layer 1: "Fehler beim Lesen"
// Layer 2: "Fehler in main()"
 
System.out.println(e.getMessage());                    // "Fehler in main()"
System.out.println(e.getCause().getMessage());         // "Fehler beim Lesen"
System.out.println(e.getCause().getCause().getMessage()); // "test.txt (No such file or directory)"

⚠️ NullPointerException!

Wenn getCause() nichts mehr findet, gibt es null zurück:

e2.getCause().getCause();  // Gibt null zurück — kein Crash hier!
// Aber:
e2.getCause().getCause().getMessage();  // 💥 NPE! .getMessage() auf null → NullPointerException

6. try-with-resources (AutoCloseable)

try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
    String line = br.readLine();
} catch (IOException e) {
    System.out.println(e.getMessage()); // "test.txt (No such file or directory)"
}
  • BufferedReader implementiert AutoCloseable → wird automatisch geschlossen.
  • Kein manuelles br.close() nötig.
  • Ressourcen werden auch bei Exception sicher geschlossen.
  • Mehrere Ressourcen: durch Komma getrennt — sie werden in umgekehrter Reihenfolge geschlossen (LIFO).

7. Best Practices

RegelWarum?
try-with-resources statt manuell closeenVerhindert Resource Leaks
Spezifische Exceptions fangen (IOException)Nicht alles mit Exception abfangen!
Bei neuem Wrappen: Message + Cause angebenBehält die ursprüngliche Info
getCause() prüfen bevor weiterVermeidet NPE
Unchecked Exceptions bewusst setzenNicht jede Ausnahme ist ein Bug — man kann auch eigene RuntimeException-Unterklassen definieren

8. Visualisierung der Chain

e ───────────────────── "Fehler in main()"          ← Layer 2 (oben)
  │
  └── getCause() ────── "Fehler beim Lesen"         ← Layer 1
        │
        └── getCause() ─ "test.txt (No such file or directory)" ← Layer 0 (unten, original)
              │
              └── getCause() → null (kein Crash!)
                    .getMessage() auf null → 💥 NPE!

9. Quick-Check-Fragen

  1. Was gibt e.getMessage() zurück? → Nur die Message der aktuellen Ebene.
  2. Was gibt e.getCause().getMessage() zurück? → Die Message der direkt darunterliegenden Ebene.
  3. Was passiert, wenn es keine nächste Ebene gibt? → getCause() = null (kein Crash!) → .getMessage() auf null → NPE!

10. Code-Beispiel: Sicheres Durchlaufen der Chain (rekursiv)

public static void printCauseChain(Exception e, int level) {
    if (e == null) return;                          // Abbruchbedingung
    System.out.println("Layer " + level + ": " + e.getMessage());
    printCauseChain(e.getCause(), level + 1);       // Rekursiver Aufruf
}
 
// Ausgabe:
// Layer 0: Fehler in main()
// Layer 1: Fehler beim Lesen
// Layer 2: test.txt (No such file or directory)

11. Verständnisaufgaben & Codeanalyse

Aufgabe A: Polymorphie verstehen

class Tier { String laut() { return "???"; } }
class Hund extends Tier { @Override String laut() { return "Wuff!"; } }
class Katze extends Tier { @Override String laut() { return "Miau!"; } }
 
Tier t = new Hund();
System.out.println(t.laut());  // Was kommt raus? Warum?

Aufgabe B: Checked Exceptions weitergeben

Was passiert, wenn eine Methode eine Checked Exception weiterwirft, ohne sie mit throws zu deklarieren oder intern abzufangen?

Aufgabe C: Mehrere Interfaces — Konflikt auflösen

interface A { default void hi() { System.out.println("A"); } }
interface B { default void hi() { System.out.println("B"); } }
class C implements A, B {}  // Was passiert hier?

Lösung: Eine Klasse muss den Konflikt zwischen zwei default-Methoden explizit auflösen:

class C implements A, B {
    @Override
    public void hi() {
        A.super.hi();  // Oder: B.super.hi(); — du entscheidest!
    }
}

Aufgabe D: Exception Chain implementieren

Schreibe eine Methode String getFullChain(Exception e), die alle Messages der Chain mit Pfeilen verbindet: "Fehler1 → Fehler2 → Original".


12. Zusammenfassung der wichtigsten Korrekturen

ThemaAlt (❌)Neu (✅)
Checked/Unchecked”Checked = von außen, Unchecked = im Code""Müssen behandelt werden / müssen nicht” — Unchecked kann auch bewusst sein
getCause()”Die nächste Ebene finden""Gibt immer die direkt umhüllte Exception zurück”
InterfacesNur default/static erwähnt+ extends, Mehrfachimplementierung, private Methoden (Java 9+)
PolymorphieOhne Overloading-VergleichDynamisch (Override) vs. Statisch (Overload) klar getrennt
NPE bei Chain”getCause() → null → CRASH!""getCause() gibt null (kein Crash!) — .getMessage() auf null → 💥 NPE”

Erstellt aus einer Lernsession über Polymorphie, Exceptions und Exception Chaining.
Verbessert nach Review: 9/10 → fachliche Korrekturen + Verständnisaufgaben ergänzt.