Java Tutorial #11

Was ist ein Java Konstruktor?

2018-11-20 | credit: adobe stock

Thema in Kurzform

Konstruktoren sind notwendig, um Objekte einer Klasse zu erzeugen

Funktion und Aufbau

Ein Konstruktor ist eine besondere Methode, die den gleichen Namen hat wie die Klasse, in der er definiert ist. Konstruktoren haben keinen Rückgabetyp (auch kein void) und müssen public sein. Ein Beispiel:

public class Adler {
    public Adler(){
        System.out.println("Konstruktor");
    }
}

Nett. Und wozu ist so ein Konstruktor gut? Ganz einfach: Konstruktoren sind für die Erzeugung von Objekten zuständig und haben dazu die wichtige Aufgabe, ein Objekt nach seiner Erzeugung in einen gültigen Zustand zu versetzen.

Der Konstruktor einer Klasse kommt nur ein einziges Mal zum Einsatz und zwar bei der Objekterzeugung. Sobald das Objekt existiert, spielt der Konstruktor keine Rolle mehr.

Wie wir schon wissen, erzeugen wir ein neues Objekt mit dem new-Operator gefolgt vom Namen der Klasse:

Adler objekt = new Adler();

Sobald Java das new-Keyword erkennt, wird Speicherplatz für das neue Objekt bereitgestellt und nach einem Konstruktor gesucht, der dann aufgerufen wird.

Wir können in einen Konstruktor alle möglichen Anweisungen packen. Diese werden dann unmittelbar nach der Objekterzeugung ausgeführt. In unserem Beispiel befindet sich eine System.out.println()-Anweisung im Konstruktor. Üblicherweise werden Konstruktoren eingesetzt, um Instanzvariablen bei der Objekterzeugung Werte zuzuweisen:

public class Adler2 {

  private String rasse;
  private String name;

  //Konstruktor
  public Adler2(String rasse, String name){
     this.rasse = rasse;
     this.name = name;
  }
}

Innerhalb des Konstruktors werden die Werte aus den Parametern den beiden Instanzvariablen rasse und name zugewiesen.

Der Aufruf des Konstruktors bei der Erzeugung eines Objekts der Klasse Adler2 sieht dann so aus:

public class Startklasse {

    public static void main(String[] args){
        Adler2 a = new Adler2("Weißkopfseeadler", "Montana");
    }
}

Wir müssen beim Aufruf des Konstruktors immer exakt die Parameter-Liste aus der Konstruktor-Definition einhalten (Datentypen und deren Reihenfolge). Ansonsten gibt es einen Compiler-Fehler und der Code wird nicht laufen. Folgender Code scheitert darum:

Adler2 a = new Adler2("Weißkopfseeadler"); // Fehler!

So können wir kein Objekt der Klasse Adler2 erzeugen, denn der Konstruktor der Klasse fordert die Übergabe von zwei String-Argumenten, wir übergeben aber nur einen String.

Kleine Zwischenübung

Bevor wir weitergehen, wollen wir das Aufrufen von Konstruktoren noch kurz einüben. Wie, glauben Sie, werden die folgenden Konstruktoren aufgerufen?

public class Eule1 {
    public Eule1(){}
}

public class Eule2 {
    public Eule2(String x, int y){}
}

public class Eule3 {
    public Eule3(boolean b){}
}

Und hier die Lösungen:

public class Startklasse {

    public static void main(String[] args){
        Eule1 e1 = new Eule1(); // keine Parameter
        Eule2 e2 = new Eule2("Bruce", 10); // Ein String, ein int-Wert
        Eule3 e3 = new Eule3(true); // ein boolescher-Wert
    }
}

Default Konstruktor

Wenn wir in unserer Klasse keinen Konstruktor von Hand selbst programmieren, beliefert uns Java beim Compilieren automatisch mit einem sogenannten Default Konstruktor (Standardkonstruktor). Dieser "unsichtbare" (da nicht im Code geschriebene) Konstruktor ist stets parameterlos und hat keinen Inhalt. Hier ein Beispiel:

public class Adler3 {
   // hier steht der unsichtbare Default Konstruktor
}

Wenn Java diesen Klassen-Code sieht, wird (ohne, dass wir davon etwas merken) der Default Konstruktor erzeugt. Dieser funktioniert genau so, als hätten wir folgenden Konstruktor definiert:

public Adler3(){}

Und nun rufen wir den Konstruktor bei der Instanziierung (Objekterzeugung) auf:

public class Startklasse {

    public static void main(String[] args){
        Adler3 a = new Adler3(); // Ruft Default Konstruktor auf
    }
}

Also, eigentlich ganz easy: Nur dann, wenn wir gar keinen Konstruktor in einer Klasse selbst schreiben, erzeugt Java freundlicherweise den Default Konstruktor nach obigm Muster. Was für ein Service 😉

Konstruktor überladen

Bisher haben wir mit Klassen gearbeitet, die jeweils nur einen Konstruktor hatten. Im Programmieralltag werden wir aber häufig den Fall haben, dass wir für eine Klasse gleich mehrere Konstruktoren benötigen. Wir sprechen dann von Überladen des Konstruktors.

Damit das funktioniert, müssen die unterschiedlichen Konstruktoren erstens alle den gleichen Namen haben wie die Klasse, in der sie definiert wurden. Sie müssen aber zweitens eine unterschiedliche Parameterliste haben.

Am Besten sehen wir uns das anhand eines konkreten Beispiels an:

public class Adler4 {

    private String rasse;
    private String name;
    private int alter;

    // Konstruktor I
    public Adler4(String rasse, String name, int alter){
        this.rasse = rasse;
        this.name = name;
        this.alter = alter;
    }

    // Konstruktor II
    public Adler4(String rasse){
        this.rasse = rasse;
        name = "unbekannt";
        alter = 0;
    }
}

Wie wir leicht erkennen, hat die Klasse Adler4 gleich zwei Konstruktoren. Sie haben denselben Namen, unterscheiden sich aber hinsichtlich ihrer Parameterliste: Konstruktor I fordert drei Parameterwerte (2x String, 1x int), Konstruktor II nur einen Parameter (1x String). Alles korrekt überladen also.

Je nachdem, welcher Konstruktor ausgeführt wird, sieht das instanziierte Objekt dann anders aus, haben die Instanzvariablen unterschiedliche Werte.

Doch welcher der beiden Konstruktoren kommt nun tatsächlich zum Einsatz? Das hängt ganz davon ab, welchen der beiden wir zur Objekterzeugung aufrufen. Denn wir haben die Wahl:

// Objekt mit Konstruktor I erzeugen:
Adler4 a1 = new Adler4("Steinadler", "Bruno", 10);

// Objekt mit Konstruktor II erzeugen:
Adler4 a2 = new Adler4("Steinadler");

Wir sehen: Ein Objekt der Klasse Adler4 wird mit Konstruktor I instanziiert, wenn wir zwei Strings und einen int-Wert (in dieser Reihenfolge!) als Argumente in die runden Klammern schreiben. Konstruktor II wird hingegen verwendet, wenn wir einen einzelnen String in die runden Klammern schreiben. Es gibt keinen dritten Weg, ein Objekt dieser Klasse zu erzeugen (auch keinen default Konstruktor). Jeder Versuch würde den Code uncompilierbar machen.

Konstruktoren verketten

Wenn wir uns die Klasse Adler4 oben ansehen, stellen wir fest, dass wir zwar unterschiedliche Konstruktoren haben, diese aber im Prinzip das Gleiche tun (nämlich die drei Instanzvariablen initialisieren). Damit verstoßen wir gegen ein Prinzip guten Programmierstils, nämlich doppelten Code unbedingt zu vermeiden (Don't repeat yourself!)

Um hier Abhilfe zu schaffen, können wir die überladenen Konstruktoren miteinander verketten.

Hierzu verwenden wir das Schlüsselwort this() als erste Anweisung innerhalb eines Konstruktors. Mit this() können wir einen Konstruktor aus einem anderen überladenen Konstruktor in der gleichen Klasse aufrufen.

Merke: Der Aufruf von this() muss immer die erste Anweisung in einem Konstruktor sein:

public class Adler5 {

    private String rasse;
    private String name;
    private int alter;

    // Konstruktor I
    public Adler5(String rasse, String name, int alter){
        this.rasse = rasse;
        this.name = name;
        this.alter = alter;
    }

    // Konstruktor II
    public Adler5(String rasse, String name){
        this(rasse, name, 0);
    }

    // Konstruktor III
    public Adler5(String rasse){
        this(rasse, "unbekannt");
    }
}

Angenommen wir erzeugen ein neues Adler5-Objekt mit Konstruktor III (das ist der mit nur einem String-Parameter), dann sieht das so aus:

Adler5 adler = new Adler5("Steinadler");

Was geschieht nun genau? Es wird innerhalb von Konstruktor III der Konstruktor II (zwei Paramter) mittels this(rasse, "unbekannt"); aufgerufen. Konstruktor IIruft dann wiederum Konstruktor I (drei Parameter) mittels this(rasse, name, 0);auf.

Bei jedem weiteren Konstruktor-Aufruf wird also ein neuer Parameter hinzugefügt, bis man schließlich bei Konstruktor I angelangt ist, der dann die ganze Arbeit erledigt, nämlich das Objekt instanziieren und den Instanzvariablen die entsprechenden Werte zuweisen (hier: "Steinadler", "unbekannt", 0).

Werbung

Java lernen

Werde zum Java Profi!

PHP Lernen

Lerne serverbasierte Programmierung

JavaScript lernen

Skille dein Webcoding

FALCONBYTE.NET

Handmade with 🖤️

© 2018-2023 Stefan E. Heller

Impressum | Datenschutz | Changelog

Falconbyte Youtube Falconbyte GitHub facebook programmieren lernen twitter programmieren lernen discord programmieren lernen