Keylearnings:
- Wie du mit Hilfe des Schlüsselworts super den Java Konstruktor einer Oberklasse aufrufst.
- Die Bedeutung des Schlüsselworts java super
- Wie du mit Hilfe von Java super eine Methode der Oberklasse aufrufst.
- Wie du mit Hilfe des Schlüsselwortes super Namenskonflikte innerhalb einer Vererbungshierarchie vermeidest.
- Der Unterschied zwischen den Schlüsselwörtern this und super.
- Wann wird ein Java Konstruktor aufgerufen?
Kopfkratzen!
Das Schlüsselwort Java super
wirkte wie Juckpulver auf meinem Kopf.
Ähnlich wie das Schlüsselwort Java this, löste es die totale Verwirrung aus!
Aber dir muss es nicht genauso gehen.
In diesem Artikel erkläre ich dir was es mit dem Java super
Schlüsselwort auf sich hat.
Wir verwenden das Schlüsselwort super
in drei Szenarien.
- Um den Java Konstruktor einer Oberklasse zu verwenden!
- Um die Methode einer Oberklasse aufzurufen!
- Um Namenskonflikte in einer Vererbungshierarchie aufzulösen!
Fällt dir was auf?
In allen Szenarien haben wir es mit einer Vererbungshierarchie zu tun.
Um das Schlüsselwort super
zu verstehen, ist es also zwingend erforderlich, dass du weißt wie Vererbung in Java funktioniert.
Beginnen wir mit dem Aufruf einer Methode in einer Oberklasse.
Java super zum Aufruf des Konstruktors einer Oberklasse
Lass uns praktisch werden und ein Beispiel betrachten. Wenn du meinen Blog schon länger liest, dann wird dir das Vierbeiner-Beispiel bekannt vorkommen.
1: public abstract class Vierbeiner { 2: protected String name = null; 3: Vierbeiner(String name){ 4: this.name = name; 5: } 6: }
Die Klasse enthält ein geschütztes Attribut name
, auf das wir aus der Vierbeiner-Unterklasse zugriff haben. Außerdem enthält die Klasse einen Konstruktor, mit dem wir das Attribut name
initialisieren.
Da es sich bei dieser Klasse um eine abstrakte Klasse handelt, können wir keine Vierbeiner-Instanzen erzeugen und die Klasse kann nur als Ableitung eines konkreten Vierbeiners verwendet werden.
Und was ist unser Lieblings-Vierbeiner? Natürlich ein Hund! Dieser hier!
Mit Hilfe des Schlüsselwortes extends
leiten wir also einen Hund von der Klasse Vierbeiner ab.
public class Hund extends Vierbeiner{ }
Auch wenn die Klasse Hund
noch keinerlei Funktionalität aufweist, sollte es doch möglich sein eine Hunde-Instanz zu erzeugen. Oder?
Starten wir einen Versuch!
Hund hund = new Hund();
Ojee, wenn wir das Programm starten erhalten wir eine Fehlermeldung.
Implicit super constructor Vierbeiner() is undefined for default constructor.
Woran liegt’s?
Die Laufzeitumgebung versucht den Standard-Konstruktor Vierbeiner()
der Klasse Vierbeiner
aufzurufen, und da dieser nicht definiert ist, wird eine entsprechende Fehlermeldung geworfen.
Aber warum geht die Laufzeitumgebung überhaupt auf die Suche nach dem Konstruktor Vierbeiner
?
Dröseln wir das mal im einzelnen auf.
Wann wird ein Java Konstruktor aufgerufen?
Obwohl unsere Klasse Hund
keinen Konstruktor besitzt, wird während der Erzeugung des Hunde-Objekts, durch die Anweisung new Hund()
der Standardkonstruktor Hund()
aufgerufen.
Ist in einer Klasse nämlich kein Konstruktor definiert, dann (und nur dann) führt der Compiler automatisch einen Standard-Konstruktor ohne Inhalt aus.
Um die Aufrufe der Konstruktoren besser verfolgen zu können, erstellen wir allerdings einen eigenen Standard-Konstruktor, im dem wir eine Bildschirmausgabe ausgeben.
public class Hund extends Vierbeiner{ public Hund(){ System.out.println("Standard-Konstruktor Hund!"); } }
Welche Auswirkung hat das Schlüsselwort extends
bei der Abarbeitung des Konstruktors?
Da wir den Hund
von der Klasse Vierbeiner
ableiten, sucht der Compiler auch hier nach dem Standard-Konstruktor Vierbeiner.
Weil diese Klasse bereits einen Konstruktor Vierbeiner(String Name)
besitzt, wird kein Konstruktor automatisch erzeugt, weshalb es zu einem Laufzeitfehler kommt.
Um das Programm wenigstens ohne Fehler starten zu können, ergänzen wir die Klasse Vierbeiner
um den Standard-Konstruktor (Zeile Drei) im folgenden Code!
1: public abstract class Vierbeiner { 2: protected String name = null; 3: public Vierbeiner(){ 4: System.out.println("Standard-Konstruktor Vierbeiner!"); 5: } 6: Vierbeiner(String name){ 7: this.name = name; 8: }
Zur besseren Nachvollziehbarkeit der Aufrufe geben wir auch hier, im Standard-Konstruktor, eine Bildschirmmeldung aus.
Beim Erzeugen einer Hunde-Instanz wird zunächst der Standard-Konstruktor des Hundes aufgerufen. Bevor dieser abgearbeitet wird, findet ein Aufruf des Standard-Konstruktor der Vierbeiner-Klasse statt.
Daher liefert die Erstellung eines Hunde-Objekts
Hund hund = new Hund();
folgende Ausgabe:
Standard-Konstruktor Vierbeiner! Standard-Konstruktor Hund!
Es wird also zuerst der Konstruktor aus der Klasse Vierbeiner
aufgerufen.
Aber mal ehrlich! Das ist einfach nur doof! 🙂
Wir haben jetzt zwar eine Hunde-Instanz. Aber eine ziemlich leere. Das einzige Attribut in unserer Klassen-Hierarchie name
hat immer den Wert null
.
Es wäre doch viel schöner, wenn wir nicht den Standard-Konstruktor sondern den Konstruktor Vierbeiner(String name)
aufrufen würden um das Attribut name
zu initialisieren.
Und hier hat das Java super
Schlüsselwort seinen großen Auftritt. Mit Java super
können wir nämlich die Konstruktoren der Oberklasse aufrufen.
Das Schlüsselwort super
wird wie eine Methode verwendet.
Okay, lass uns den Konstruktor Vierbeiner(String name)
aufrufen.
Hierfür müssen wir den Standard-Konstruktor wie folgt anpassen:
1: public Hund(){ 2: super("Bello"); 3: System.out.println("Standard-Konstruktor Hund!"); 4:}
In der zweiten Zeile rufen wir Java super
mit dem Parameter „Bello“ auf. Das führt dazu, dass der Compiler in der Oberklasse nach einem Java Konstruktor mit EINEM String Parameter in der Parameterliste sucht und diesen ausführt.
Genau wie beim überschreiben einer Methode, wird auch beim Schlüsselwort super
die Parameterliste verwendet um den richtigen Konstruktor aufzurufen.
Bei unserem Beispiel führt das zu einer Ausführung des Java Konstruktors Vierbeiner(String name)
, der das Attribut name
initialisiert.
Wichtig ist auch hier, dass zuerst der Konstruktor aus der Oberklasse abgearbeitet wird. Aus diesem Grund muss das Schlüsselwort super
die erste Anweisung im Java Konstruktor Hund()
sein.
Naja, so richtig toll ist das noch immer nicht. Schließlich wollen wir auch Hunde erzeugen, die nicht Bello heißen.
Hierfür fügen wir der Klasse Hund einen weiteren Java Konstruktor hinzu, dem wir den Namen des zu erzeugenden Hundes als Parameter mitgeben können.
Allerdings befindet sich das Attribut name
nicht in der Klasse Hund sondern in der Oberklasse Vierbeiner
und wird mit dem Konstruktor dieser Klasse initialisiert.
Du ahnst es sicher schon womit wir dieses Problem lösen. Genau! Mit Java super
schleusen wir den String Parameter, der den Namen des Hundes enthält, bis in die Oberklasse durch.
1: public class Hund extends Vierbeiner{ 2: public Hund(){ 3: super("Bello"); 4: System.out.println("Standard-Konstruktor Hund!"); 5: } 6: Hund(String name){ 7: super(name); 8: } 9:}
Jetzt können wir beim Erzeugen eines Hunde-Objektes dem Java Konstruktor den Namen des Hundes als Parameter übergeben.
Hund hund = new Hund("Hugo");
Die Anweisung new Hund("Hugo")
ruft den Konstruktor Hund(String name)
aus der Klasse Hund
auf. Das erste was hier gemacht wird, ist über das Schlüsselwort super
den Java Konstruktor Vierbeiner(String name)
mit dem Argument „Hugo“ aufzurufen (Zeile 7), womit das Vierbeiner
Attribut name
mit dem String Hugo initialisiert wird.
Auf diese Weise haben wir ein Hunde-Objekt mit dem Namen Hugo erzeugt.
Wichtig hierbei ist, dass die Instanziierung eines Objektes von oben nach unten erfolgt. In unserem Fall wird also zuerst der Konstruktor aus der Klasse Vierbeiner
abgearbeitet und erst dann der Konstruktor aus der Klasse Hund
ausgeführt.
Das hat zur Folge, dass das Schlüsselwort super
immer die erste Anweisung innerhalb eines Java Konstruktor sein muss.
Soviel zu dem Aufruf eines Konstruktors einer Oberklasse. Wenden wir uns dem nächsten Einsatzgebiet von Java super
zu. Nämlich dem Aufruf von Methoden einer Oberklasse.
Java super zum Aufruf einer Methode in einer Oberklasse
Wir haben uns hier bereits über das Überschreiben von Methoden einer Oberklasse unterhalten.
Beim Überschreiben einer Methode lösen wir die Methode der Oberklasse vollständig durch die (gleichnamige) Methode einer Unterklasse ab.
Ziel einer Vererbungshierarchie ist meist jedoch nicht die Ersetzung einer Funktionalität sondern vielmehr die Verfeinerung.
In diesem Fall sprechen wir manchmal auch von einer Verkettung.
Noch nicht klar? Okay, nicht schlimm. Schauen wir uns das ganze anhand eines Beispiels an.
Vierbeiner sind hungrig und müssen fressen. Daher ist es ein guter Plan unsere Vierbeiner
Klasse um eine fressen
Methode zu erweitern.
public void fressen(){ System.out.println("Ich habe hunger!"); }
Diese Methode macht nichts weiter als eine Bildschirmausgabe zu erzeugen!
Worin unterscheidet sich die fressen()
Methode eines allgemeinen Vierbeiners von der eines Hundes?
Genau! Ein Hund bittet zusätzlich zivilisiert um einen Fressnapf!
Wie können wir unsere Klasse Hund um diese Funktionalität erweitern?
Zunächst die Art, auf die du es nicht machen solltest!
Wir kopieren die fressen()
Methode aus der Klasse Vierbeiner
in die Klasse Hund
und ergänzen diese Methode um eine entsprechende Bildschirmausgabe. Also:
public void fressen(){ System.out.println("Ich habe hunger!"); System.out.println("Bitte bring mir einen Napf!"); }
Das entspricht dem klassischen Überschreiben einer Methode.
Auch wenn es in unserem Minibeispiel kaum auffällt, hat diese Vorgehensweise einen großen Nachteil, den wir in der objektorientierten Programmierung vermeiden wollen.
Durch das Kopieren der Methode erzeugst du doppelten Programmcode!
Viel besser wäre es doch, wenn wir die Funktionalität der fressen()
Methode in der Oberklasse Vierbeiner
vollständig verwenden könnten und lediglich um das zusätzliche Verhalten eines Hundes ergänzen würden.
Und genau das können wir mit Hilfe von Java super
erreichen! Mit der Hilfe des Schlüsselwortes super
können wir nämlich aus der fressen()
Methode des Hundes die fressen()
Methode des Vierbeiners aufrufen.
Im folgenden die fressen()
Methode der Klasse Hund
unter Verwendung von Java super
.
1: public void fressen(){ 2: super.fressen(); 3: System.out.println("Bitte bring mir einen Napf!"); 4:}
In Zeile zwei rufen wir mit Hilfe von super
die fressen()
Methode der Oberklasse Vierbeiner
auf, welche die Bildschirmausgabe
Ich habe hunger!
erzeugt.
Nachdem die fressen()
Methode in der Klasse Vierbeiner
abgearbeitet ist, wird die Verarbeitung in der gleichnamigen Methode der Klasse Hund
in Zeile Drei fortgesetzt, was zur Bildschirmausgabe
Bitte bring mir einen Napf!
führt!
Genau wie beim Aufruf eines Java Konstruktor einer Oberklasse, werden auch hier die Methoden innerhalb der Vererbungshierarchie von oben nach unten abgearbeitet.
In unserem Beispiel wird also zuerst die fressen()
Methode der Vierbeiner-Klasse ausgeführt und erst danach die entsprechende Methode aus der Klasse Hund
.
Das hat zur Folge, dass auch hier das Schlüsselwort super
die erste Anweisung innerhalb einer Methode sein muss.
Noch eine Warnung. Nicht zu Hause nach machen
Man könnte auf die Idee kommen die Methoden der Oberklasse über deren Namen aufzurufen. In unserem fressen()
Beispiel würde das folgendermaßen aussehen.
1: public void fressen(){ 2: fressen(); 3: System.out.println("Bitte bring mir einen Napf!"); 4:}
Das hat aber katastrophale folgen, da sich in Zeile Zwei die Methode unendlich oft selber aufruft. Der Methodenaufruf in Zeile Zwei wird so interpretiert als stünde das Schlüsselwort this vor dem Methodenaufruf.
Der Compiler interpretiert die zweite Zeile also als this.fressen()
.
Soviel zu dem Aufruf von Methoden einer Oberklasse. Zu guter letzt wollen wir der Vollständigkeit wegen noch ein weiteres Einsatzgebiet von Java super
besprechen.
Java super zum Auflösen von Namenskonflikten in einer Vererbungshierarchie
Genau wie wir Methoden einer Oberklasse mit Hilfe von super
aufrufen können, kann Java super
auch zum Zugriff auf die Attribute einer Oberklasse verwendet werden.
Da wir allerdings gelernt haben, dass wir auf Attribute einer Klasse mit Hilfe von getter- und setter- Methoden zugreifen sollten, werden wir das in der Realität eher selten machen.
Dennoch wollen wir uns ein kleines Beispiel ansehen, das uns außerdem noch einmal den Unterschied zwischen den Schlüsselwörtern Java this und super
vor Augen führt.
1: class A{ 2: public int x = 10; 3:} 4: class B extends A{ 5: public int x = 100; 6:}
Das Beispiel besteht aus zwei Klassen A
und B
wobei B
eine Unterklasse von A
ist. Beide Klassen enthalten das gleichnamige Integer-Attribut x
.
Unser Ziel ist es die Klasse B
um eine Methode printAttributes
zu ergänzen, die das Attribut x
beider Klassen auf dem Bildschirm ausgibt.
Aber wie lösen wir den Namenskonflikt auf?
Genauer wie legen wir fest, ob wir das Attribut x
aus der Klasse A
oder B
meinen?
Diesen Job übernehmen für uns die Schlüsselwörter Java this und super
.
Wir ergänzen die Klasse B
um eine Methode printAttributes
.
1: public void printAttributes(){ 2: System.out.println(this.x); 3: System.out.println(super.x); 4:}
Welche Bildschirmausgabe erwartest du, wenn wir diese Methode aufrufen?
In der zweiten Zeile geben wir das Attribut x
mit vorangestelltem this aus, da wir die Methode printAttributes
in der Klasse B
definiert haben, bezieht sich das Schlüsselwort this auf diese Klasse und es wird das Attribut x
aus der Klasse B
ausgegeben.
Da wir in Zeile Drei das Schlüsselwort super
dem Attribut x
voranstellen beziehen wir uns hier auf die Oberklasse A
.
Somit liefert die Methode printAttributes
folgende Bildschirmausgabe:
100 10
Fazit: In diesem Artikel hast du die Anwendungen des Java Schlüsselwortes super
kennengelernt. Mit Hilfe dieses Schlüsselworts kannst du zum einen den Java Konstruktor einer Oberklasse aufrufen und zum anderen eine Methode einer Oberklasse verfeinern. Außerdem kannst du mit Hilfe dieses Schlüsselwortes Namenskonflikte innerhalb einer Vererbungshierarchie vermeiden.
Ich freue mich auf deine Fragen im Kommentarbereich!
Hat dir der Artikel gefallen? Dann folge uns doch am besten gleich auf Facebook!
Khalid Mohadi
19. Mai 2017 at 14:28Klasse Erklärung. Auch wenn ich Hunde hasse, hat das Beispiel mit den Vierbeinern einiges klar gemacht.
Weiter so!
Kim Peter
21. Mai 2017 at 21:33Hi Khalid, danke für dein Feedback! Ein anderes Beispiel, an dem du das ganze veranschaulichen kannst ist das Beispiel Fahrzeug. Von einem Fahrzeug könntest du Beispiel ein Auto ein LKW, oder ein Moped ableiten und in diesen Klassen mit Hilfe des Schlüsselworts super Methoden und Konstruktoren der Fahrzeug Oberklasse aufrufen. Viele Grüße Kim
Arthur
17. Juni 2017 at 12:53Hat mir sehr weitergeholfen ! Vielen Dank ,bitte weiter so 🙂
Kim Peter
17. Juni 2017 at 16:48Hallo Arthur, vielen Dank für dein Feedback. Freue mich wenn ich weiterhelfen kann. Viele Grüße Kim
Timy
6. Januar 2018 at 23:30Hey Kim,
Ich bin Informatikstudent und kenne mich daher relativ gut aus in Java . Trotzdem nutze ich deinen Blog absolut gerne um immer wieder mal was nachzulesen, da man mit der Zeit vieles auch vergisst. Mach weiter so! Deine Erklärungen sind manchmal besser wie die der Profs.
Grüße
Timy
Kim Peter
17. Januar 2018 at 12:19Hi Timy, vielen Dank! Ja, es geht bald hier weiter. Viele Grüße Kim
Nanek
12. April 2018 at 20:35Super Erklärung Danke. Schreibe Montag mein Abitur und das hat mir sehr weitergeholfen
Kim Peter
13. April 2018 at 7:21Dann wünsche ich dir viel Erfolg für dein Abitur! Viele Grüße Kim
Mario
12. August 2018 at 11:12Hallo, aber was hat das Super bei z.b forEach für eine Bedeutung ?
Bei Iterable (Java 8): forEach(Consumer action)
Kim Peter
8. September 2018 at 19:48Hallo Mario, habe ich ehrlich gesagt noch nie verwendet, aber es A super B bedeutet, dass A eine Oberklasse von B sein muss. Ist also quasie das gleiche wie B extends A. Viele Grüße Kim
Jason
7. Juni 2019 at 11:32Ich wollte mich nur für die tolle Erklärung bedanken!
Kim Peter
2. Juli 2019 at 11:24Hallo Jason, sehr gerne! Viele Grüße Kim
Clara
28. August 2019 at 8:09Hey Kim! Ich lerne gerade für eine Informatikklausur und du rettest mich mit deinem Blog mega.
Liebe Grüße und weiter so!
Kim Peter
30. August 2019 at 6:13Hi Clara, ich danke dir! Viele Grüße Kim
Roger
10. Januar 2020 at 11:37Hallo Kim,
ich bin ein Neuling auf der Insel und schon oft über super und this gestolpert. Ich bin zwar vertraut mit dem Konzept der Vererbung, mir war aber nie so richtig klar, wie man damit in Java umgeht.
Dank deines sehr anschaulichem Beispiel habe ich es jetzt begriffen.
Danke für deine Mühe !
Liebe Grüße Roger
Kim Peter
16. Januar 2020 at 7:30Hallo Roger, vielen Dank für dein Feedback! Viele Grüße Kim
Eva
26. April 2020 at 9:47Dein Blog ist wirklich hilfreich. Danke!
Kim Peter
1. Mai 2020 at 12:17Freut nich wenn ich helfen kann. Viele Grüße Kim
Stefan
2. Mai 2020 at 18:03Hallo Kim,
vielen Dank dafür, endlich verstehe ich das auch!
Ich hab mir Deinen blog jedenfalls gebookmarked.
Gruß
Stefan
Kim Peter
3. Mai 2020 at 19:01Hallo Stefan, vielen Dank für das bookmarken! 🙂 Viele Grüße Kim
Frank Mnich
10. Mai 2020 at 9:53Vielen Dank für die ausführliche Erklärung. Habe schon mehrfach von Deinem tollen Blog profitiert. Bitte weiter so…
Gruß
Frank
Kim Peter
19. Mai 2020 at 18:29Hallo Frank, vielen Dank für dein Feedback. Freue mich, wenn ich helfen kann. Viele Grüße Kim
Choose a style: