Alle Tabs der Lerneinheit (Erklärung · Interaktiv · Quiz) als durchgehender Text. Ideal zum Wiederholen vor der Klausur, und für Suchmaschinen wie Google, Bing und KI-Suche (ChatGPT, Perplexity).
Erklärung
Vererbung ermöglicht es, eine Klasse von einer anderen abzuleiten und ihre Eigenschaften und Methoden zu übernehmen. Polymorphie heißt: derselbe Methodenaufruf verhält sich unterschiedlich, je nachdem welche konkrete Klasse das Objekt hat. Beispiel: tier.machGeraeusch() ruft bei einem Hund "Wuff!" und bei einer Katze "Miau", je nach echtem Objekttyp. Du lernst hier, wann Vererbung sinnvoll ist (is-a-Beziehung), wie extends und super funktionieren, wie Methoden mit @Override überschrieben werden, warum bei polymorphen Aufrufen der echte Objekttyp entscheidet (Dynamic Dispatch) und welche Sonderfälle static, private, final, abstract und Interfaces in Java haben. Zwei zentrale OOP-Mechanismen, die in jeder Klausur abgefragt werden.
Was du können musst:
- extends (Java):
class Hund extends Tiermacht Hund zur Subklasse von Tier - super: Aufruf der Konstruktor- oder Methoden-Implementierung der Oberklasse
- Override: Subklasse ersetzt eine Methode der Oberklasse, oft mit @Override-Annotation
- abstract:
abstract classkann nicht direkt mitnewerzeugt werden.abstract methodhat keinen Body und muss in konkreten Subklassen implementiert werden; eine abstrakte Subklasse darf abstrakte Methoden weiter offenlassen. (Häufiger Sprachfehler: man sagt nicht "Methode instanziieren", sondern "Klasse instanziieren" oder "Methode implementieren/überschreiben".) - Polymorphie: Variable vom Typ
Tierkann auf einHund-Objekt zeigen; aufrufbar sind nur Methoden, die im deklarierten TypTierbekannt sind, bei überschriebenen Instanzmethoden läuft dann die Hund-Version
In Klausuren wirst du oft gefragt: welche Methode wird aufgerufen? wenn eine Variable vom Obertyp auf ein Subtyp-Objekt zeigt. Antwort: bei überschriebenen Instanzmethoden entscheidet der echte Objekttyp zur Laufzeit (dynamic dispatch). Ausnahmen: static (versteckt statt überschrieben), private (nicht überschreibbar), final (kann nicht überschrieben werden) sind nicht polymorph im Override-Sinn. Außerdem prüft Java Methodenaufrufe zuerst anhand des deklarierten Variablentyps: nur wenn die Methode dort bekannt ist, kann dynamic dispatch zur Override-Version greifen.
Das Problem
Du programmierst eine Tier-App. Du hast schon eine Klasse Hund:
class Hund {
String name;
int alter;
void schlafen() { System.out.println("Zzz..."); }
void machGeraeusch() { System.out.println("Wuff!"); }
}
Jetzt brauchst du Katze. Schreibst du die ganze Klasse nochmal? Mit name, alter, schlafen(), nochmal alles?
Vererbung sagt: nein. Pack das Gemeinsame in eine Basisklasse Tier, lass Hund und Katze davon erben.
class Tier {
String name;
int alter;
void schlafen() { System.out.println("Zzz..."); }
}
class Hund extends Tier {
void machGeraeusch() { System.out.println("Wuff!"); }
}
class Katze extends Tier {
void machGeraeusch() { System.out.println("Miau"); }
}
Hund hat jetzt automatisch
name,alterundschlafen(), geerbt von Tier. Plus seine eigene MethodemachGeraeusch().
Das ist die Grundidee: gemeinsamer Code wandert nach oben, jede Unterklasse fügt nur ihr Spezifisches dazu.
Die Begriffe
| Begriff | Was es bedeutet | Beispiel |
|---|---|---|
| Basisklasse / Superklasse / Parent | Die Klasse oben in der Hierarchie | Tier |
| Subklasse / Unterklasse / Child | Die Klasse, die erbt | Hund, Katze |
| extends (Java) / Klammern (Python) | Vererbungs-Syntax | class Hund extends Tier |
| Vererbung | Subklasse übernimmt die zugänglichen Felder und Methoden der Basisklasse. Private Felder sind im Objekt vorhanden, aber aus der Subklasse nicht direkt zugreifbar (Zugriff nur über public/protected Methoden der Basisklasse). Konstruktoren werden NICHT geerbt (weder direkt noch indirekt); jede Subklasse mit eigener Initialisierung oder parametrisiertem Basis-Konstruktor definiert eigene Konstruktoren, die typischerweise mit super(...) den Basisklassen-Konstruktor aufrufen | Hund hat name |
| Override | Subklasse ersetzt eine geerbte Methode mit eigener Version | Hund.machGeraeusch() |
| super (Java) / super() (Python) | Zugriff auf die Methode/Konstruktor der Basisklasse | super(name, alter) |
| is-a Beziehung | "Ein Hund ist ein Tier", Faustregel für Vererbung | Hund is-a Tier ✓, Hund is-a Auto ✗ |
Vererbung modelliert "ist ein". Wenn du nicht sagen kannst "X ist ein Y", dann gehört X auch nicht in Y rein.
Code: extends (Java) und (Python)
class Tier {
String name;
int alter;
Tier(String name, int alter) {
this.name = name;
this.alter = alter;
}
void schlafen() {
System.out.println(name + " schläft: Zzz...");
}
void machGeraeusch() {
System.out.println("(unspezifisches Geräusch)");
}
}
class Hund extends Tier {
String rasse;
Hund(String name, int alter, String rasse) {
super(name, alter); // Konstruktor der Basisklasse aufrufen
this.rasse = rasse;
}
@Override
void machGeraeusch() { // überschreibt Tier.machGeraeusch()
System.out.println(name + ": Wuff!");
}
}
// Nutzung
Hund bello = new Hund("Bello", 4, "Labrador");
bello.schlafen(); // → "Bello schläft: Zzz..." (geerbt von Tier)
bello.machGeraeusch(); // → "Bello: Wuff!" (von Hund überschrieben)
In Python:
class Tier:
def __init__(self, name, alter):
self.name = name
self.alter = alter
def schlafen(self):
print(f"{self.name} schläft: Zzz...")
def mach_geraeusch(self):
print("(unspezifisches Geräusch)")
class Hund(Tier):
def __init__(self, name, alter, rasse):
super().__init__(name, alter)
self.rasse = rasse
def mach_geraeusch(self):
print(f"{self.name}: Wuff!")
bello = Hund("Bello", 4, "Labrador")
bello.schlafen() # → "Bello schläft: Zzz..."
bello.mach_geraeusch() # → "Bello: Wuff!"
super()ist der Schlüssel: damit ruft die Subklasse die Methode der Basisklasse auf, typischerweise im Konstruktor, um Felder der Basisklasse zu setzen.
Method-Dispatch: welche Methode wird aufgerufen?
Stell dir vor du rufst bello.machGeraeusch() auf. Welche Methode läuft?
Regel: Schau zuerst in der konkreten Klasse der Instanz (also Hund), dann wandere die Hierarchie hoch zur nächsten Basisklasse.
bello.machGeraeusch()
↓
1. Hat Hund eine machGeraeusch()? → Ja → AUSFÜHREN ("Wuff!")
bello.schlafen()
↓
1. Hat Hund eine schlafen()? → Nein
2. Hat Tier eine schlafen()? → Ja → AUSFÜHREN ("Zzz...")
bello.fliegen()
↓
1. Hat Hund fliegen()? → Nein
2. Hat Tier fliegen()? → Nein
3. Compile-Fehler! (Java)
AttributeError! (Python)
Erst die spezifischste Klasse, dann nach oben, die erste passende Methode gewinnt.
Wichtig zum Zusammenspiel mit Polymorphie: Java prüft zur Compile-Zeit, ob die Methode im deklarierten Typ der Variable bekannt ist. Nur wenn ja, greift zur Laufzeit Dynamic Dispatch zur Override-Version. Beispiel: bei Tier x = new Hund(...); x.fliegen() ist es ein Compile-Fehler, weil Tier keine fliegen() kennt, obwohl Hund vielleicht eine hätte. Im Interaktiv-Tab kannst du das live durchspielen.
Polymorphie: ein Typ, viele Formen
Das ist der wirkliche Trick. Du kannst eine Variable vom Typ Tier haben, die in Wirklichkeit einen Hund speichert:
Tier x = new Hund("Bello", 4, "Labrador");
x.machGeraeusch(); // → "Bello: Wuff!"
Was? x ist als Tier deklariert, aber Wuff! kommt raus? Ja, das ist dynamischer Dispatch:
Welche Methode aufgerufen wird, entscheidet der echte Typ des Objekts (Hund), nicht der deklarierte Typ der Variable (Tier).
Das nennt man Polymorphie, "viele Formen". Eine Tier-Variable kann verschiedene Formen annehmen (Hund, Katze, Vogel) und sich entsprechend verhalten.
Tier[] zoo = {
new Hund("Bello", 4, "Labrador"),
new Katze("Whiskers", 3),
new Vogel("Tweety", 2)
};
for (Tier t : zoo) {
t.machGeraeusch(); // jedes Tier sein eigenes Geräusch
}
// Output:
// Bello: Wuff!
// Whiskers: Miau
// Tweety: Tschilp
Polymorphie ist der Hauptgrund warum Vererbung mächtig ist: gleicher Code, unterschiedliches Verhalten je nach echtem Typ.
Override richtig: @Override und super.
In Java schreibt man @Override über eine überschriebene Methode. Das ist optional, aber stark empfohlen, der Compiler prüft, dass die Methode wirklich existiert in der Basisklasse.
class Hund extends Tier {
@Override
void machGeraeusch() { // Compiler: passt das zu Tier.machGeraeusch()? Ja!
System.out.println("Wuff!");
}
}
Verschreibst du dich (mach_Geraeusch() mit Tippfehler), wird ohne @Override einfach eine neue Methode angelegt, Bug auf Lebenszeit. Mit @Override schreit der Compiler.
In Python gibt es kein Java-gleiches Compile-Time-@Override. Ab Python 3.12 gibt es typing.override als Decorator für statische Typchecker, zur Laufzeit bleibt Python aber dynamisch und erzwingt Override nicht.
super. innerhalb einer Override
Manchmal willst du das alte Verhalten ergänzen, nicht ersetzen:
class Hund extends Tier {
@Override
void schlafen() {
super.schlafen(); // Tier.schlafen() rufen
System.out.println("(im Korb)");
}
}
// Hund.schlafen() → "Bello schläft: Zzz..." + "(im Korb)"
super.methode()heißt: "Ruf die Version der Basisklasse auf." Häufig in Konstruktoren (super(name, alter)) und beim Erweitern statt Ersetzen.
abstract: Methoden ohne Body
Manchmal soll die Basisklasse erzwingen, dass jede Subklasse eine bestimmte Methode hat, ohne selbst zu wissen, was die tut.
abstract class Tier {
String name;
abstract void machGeraeusch(); // kein Body; konkrete Subklassen MÜSSEN implementieren
void schlafen() { // normal, kann geerbt werden
System.out.println(name + " schläft");
}
}
class Hund extends Tier {
@Override
void machGeraeusch() {
System.out.println("Wuff!"); // Pflicht: Compiler erzwingt es
}
}
new Tier(); // FEHLER: kann nicht instantiiert werden, Tier ist abstract
new Hund(); // OK
Eine abstrakte Klasse kann nicht direkt instantiiert werden, nur ihre konkreten Subklassen. Sie definiert ein Pflichtprogramm. Wichtig: eine abstrakte Klasse kann sowohl abstrakte Methoden (ohne Body, MUSS überschrieben werden) als auch konkrete Methoden (mit Body, wird geerbt) enthalten.
Klausur-Tricks
Trick 1, is-a vs has-a: Vererbung ist "ist ein". Wenn du eher sagen würdest "hat ein", brauchst du Komposition (Feld vom Typ X), nicht Vererbung.
Hund is-a Tier ✓ → Vererbung
Auto has-a Motor ✓ → Komposition (Auto hat ein Motor-Feld)
Auto is-a Motor ✗ → falsche Modellierung
Trick 2, Override-Regel (Java): Eine Methode in der Subklasse überschreibt nur, wenn:
- gleicher Name
- gleiche Parameter-Liste (sonst ist es eine Überladung, anderes Konzept)
- gleicher oder kovarianter Rückgabetyp
- Sichtbarkeit nicht einschränken (z. B. von
publicaufprotectedist nicht erlaubt) - geprüfte Exceptions nicht breiter machen
Trick 3, Konstruktor wird NICHT geerbt. Du musst in der Subklasse einen eigenen Konstruktor schreiben und mit super(...) den Basis-Konstruktor aufrufen.
Trick 4, Polymorphie-Vergleich (nur als Sprachen-Vergleich, falls deine Klausur mehrere Sprachen erwähnt):
- Java: dynamic dispatch für überschreibbare Instanzmethoden, nicht für
static/private/final. - Python: dynamische Methodensuche über die MRO (Method Resolution Order).
- C++: dynamic dispatch nur bei
virtual-Methoden.
Klausur-Frage: "ist equals() in Java polymorph?" → Ja. Da Object.equals() eine public instance method ist (nicht static/private/final), wird sie polymorph überschrieben, z. B. in String, ArrayList, eigenen Klassen.
Trick 5, Diamant-Problem: Mehrfachvererbung von Klassen ist in Java verboten (nur 1 extends). Mehrere Interfaces sind erlaubt; seit Java 8 können Interfaces default-Methoden mit Body haben, bei Konflikten muss die Klasse eindeutig überschreiben oder auflösen. Python erlaubt Mehrfachvererbung. In Python 3 sind alle Klassen new-style, und die MRO (Method Resolution Order, C3-Linearisierung) löst Konflikte über eine feste Suchreihenfolge der Klassenhierarchie. (Python 2 old-style classes nutzten einfache Tiefensuche, sind aber seit 2020 EOL.)
Trick 6, Java spezifisch: Object ist Vater aller Klassen. Jede Klasse erbt implizit von java.lang.Object, daher hat jedes Objekt toString(), equals(), hashCode().
Trick 7, instanceof: prüft den echten Typ zur Laufzeit:
Tier t = new Hund(...);
t instanceof Hund // true, echter Typ ist Hund
t instanceof Tier // true, Hund ist auch ein Tier (Vererbungskette)
t instanceof Katze // false
Wo brauchst du Vererbung & Polymorphie?
- GUI-Frameworks:
Button extends Widget,TextField extends Widget, alle habenrender()polymorph - Game-Entwicklung:
Enemy extends GameObject, viele Enemy-Subtypes mit eigenemupdate() - Servlets / Spring:
MyController extends BaseControllermit überschriebenen Endpoints - Sammlungen:
ArrayList implements List,LinkedList implements List, gleiches Interface, andere Implementierung - Streams in Java:
FileInputStream extends InputStream,BufferedInputStream extends InputStream
Faustregel: Wenn du in der Klausur sagen sollst, ob Vererbung passt, frag dich "ist X ein Y?" Wenn ja, vererbe. Wenn nein, finde was anderes (Komposition, Interface, etc.).
Interaktiv
Hierarchie-Spielwiese
Drei Tier-Klassen erben von Tier. Jede überschreibt machGeraeusch(), schlafen() wird geerbt (nicht überschrieben), fliegen() gibt's nur in Vogel.
Probier folgendes:
h.machGeraeusch(), Dispatch findet Methode in Hund (überschrieben), Output "Wuff!"k.schlafen(), Dispatch findet nichts in Katze, geht hoch zu Tier, Output "Zzz..."h.fliegen(), Dispatch findet nichts in Hund, nichts in Tier → Fehler, Methode existiert nichtv.machGeraeusch(), Vogel überschreibt, Output "Tschilp"
Beobachte den Dispatch-Pfad: rote Box = hier gefunden, durchgestrichene Boxen = hier gesucht aber nicht gefunden.
Interaktive Visualisierung
Visualisiert Vererbungs-Hierarchien mit überschriebenen Methoden und dynamischem Dispatch.
Faustregel zum Mitnehmen: Bei überschreibbaren Instanzmethoden geht die Methodensuche vom echten Typ nach oben. Suche zuerst in der konkreten Klasse, wandere dann durch extends hoch. Erste passende Methode wird ausgeführt, das ist der dynamische Dispatch-Mechanismus, die Basis von Polymorphie. (Sonderfälle: static, private, final sind nicht polymorph.)
Code-Stepper: Klausur-Klassiker durchspielen
Drei interaktive Demos zu den häufigsten Klausur-Stolperern bei Vererbung und Polymorphie: Dynamic Dispatch (welche Methode wird aufgerufen, wenn die Variable als Tier deklariert ist, aber auf einen Hund zeigt?), Constructor-Chaining mit super() (Reihenfolge: Basisklasse-Konstruktor läuft zuerst), und Polymorphes Array mit Tier-Array, das verschiedene konkrete Typen enthält. Klicke auf Step oder Auto.
1. Dynamic Dispatch: welche Methode läuft?
Interaktive Visualisierung
Interaktive Komponente: probiere sie im Topic-Player oben aus.
2. Constructor-Chaining: super() läuft ZUERST
Wenn eine Subklasse eine Instanz erzeugt, läuft der Basisklassen-Konstruktor vor dem eigenen Konstruktor-Body. super(...) macht das explizit; ohne expliziten Aufruf nimmt Java automatisch den No-Args-Konstruktor der Basisklasse.
Interaktive Visualisierung
Interaktive Komponente: probiere sie im Topic-Player oben aus.
3. Polymorphes Array: ein Typ, viele Formen
Ein Array vom Typ Tier[] kann Hunde, Katzen und Vögel halten. Die for-Schleife ruft machGeraeusch() auf, und Dynamic Dispatch sorgt dafür, dass die richtige Methode pro Element läuft.
Interaktive Visualisierung
Interaktive Komponente: probiere sie im Topic-Player oben aus.
Quiz
Klausurfragen mit Lösungen (8)
- F1.Welche der folgenden Beziehungen rechtfertigt Vererbung (is-a)?
Antwort: Hund und Tier
Erklärung: Vererbung modelliert 'ist ein' (is-a). 'Hund ist ein Tier' ✓. Die anderen sind 'hat ein' (has-a), Komposition wäre richtig: Auto hat einen Motor, Mensch hat eine Adresse.
- F2.Was passiert, wenn eine Subklasse eine Methode der Basisklasse mit gleicher Signatur definiert?
Antwort: Die neue Methode überschreibt die alte (Override)
Erklärung: Das ist **Override**: gleiche Signatur in der Subklasse ersetzt die geerbte Methode. Bei Aufrufen auf einer Subklassen-Instanz wird die neue Version genommen, egal ob die Variable als Sub- oder Basisklasse deklariert ist (dynamischer Dispatch).
- F3.In Java: was bewirkt super(name, alter) im Konstruktor einer Subklasse?
Antwort: Ruft den Konstruktor der Basisklasse auf, um geerbte Felder zu initialisieren
Erklärung: super(...) ruft den Konstruktor der Basisklasse mit den angegebenen Argumenten auf. Damit werden die geerbten Felder ordentlich initialisiert. Konstruktoren werden NICHT geerbt, daher braucht jede Subklasse einen eigenen.
- F4.Was gibt bello.machGeraeusch() aus, wenn Hund machGeraeusch() mit 'Wuff!' überschreibt?
Antwort: Wuff!, weil dynamischer Dispatch den echten Typ Hund nimmt
Erklärung: Polymorphie / dynamischer Dispatch: Java guckt zur Laufzeit auf den **echten** Typ des Objekts (Hund), nicht auf die Variablen-Deklaration (Tier). Hund hat machGeraeusch() überschrieben → 'Wuff!' wird ausgegeben. **Voraussetzung**: Die Methode muss im **deklarierten** Typ (hier `Tier`) bekannt sein, sonst wäre der Aufruf bereits Compile-Fehler. Dynamic dispatch entscheidet nur, **welche** Override-Version einer im Typ bekannten Methode läuft.
- F5.Eine Klasse ist als abstract deklariert. Was bedeutet das?
Antwort: Sie kann nicht direkt instantiiert werden, nur konkrete Subklassen können es
Erklärung: abstract heißt: kein direktes 'new'. Die Klasse beschreibt ein Konzept, das erst durch konkrete Subklassen Form annimmt. Sie kann sehr wohl Felder und Methoden haben, und meist auch abstract-Methoden, die Subklassen implementieren MÜSSEN.
- F6.Was passiert beim Method-Dispatch, wenn die Methode weder in der konkreten Klasse noch in einer Basisklasse existiert?
Antwort: Compile-Fehler in Java, AttributeError zur Laufzeit in Python
Erklärung: In Java findet der Compiler den Aufruf gar nicht erst zulässig, Build schlägt fehl. In Python merkt's der Interpreter erst zur Laufzeit (AttributeError), weil Python dynamisch typisiert ist. So oder so: kein automatisches Erstellen, kein null.
- F7.Warum erlaubt Java keine Mehrfachvererbung von Klassen (also nicht class C extends A, B)?
Antwort: Wegen des Diamant-Problems: Konflikt, wenn A und B beide eine Methode m() haben, welche soll C erben?
Erklärung: Diamant-Problem: erbt C von A und B, und beide haben m(), welche soll gelten? Java verbietet **Klassen**-Mehrfachvererbung. Mehrere **Interfaces** sind erlaubt, historisch hatten sie keinen Body, aber **seit Java 8** können Interfaces \`default\`-Methoden mit Implementierung haben. Bei Default-Method-Konflikten muss die Klasse eindeutig überschreiben/auflösen. Python erlaubt's und löst per **MRO** (in Python 3 immer C3-Linearisierung).
- F8.Was ist Polymorphie in einem Satz?
Antwort: Eine Variable vom Basisklassen-Typ kann sich je nach echtem Objekttyp unterschiedlich verhalten
Erklärung: Polymorphie = 'viele Formen'. Eine Tier-Variable kann ein Hund, eine Katze, ein Vogel sein, bei einem Methodenaufruf wird die jeweils passende Override-Version ausgeführt. Das macht Code flexibel: gleicher Aufruf, unterschiedliches Verhalten je nach Typ.
Tier bello = new Hund("Bello", 4, "Labrador");
bello.machGeraeusch();