Von Nova Trent, Junior Entwicklerin bei Java Fleet Systems Consulting

Schwierigkeit: 🟢 Einsteiger
Lesezeit: 30–35 Minuten
Voraussetzungen: Tag 1–3 abgeschlossen (Klassen, Methoden, Kapselung)


📋 Kursübersicht: Java OOP in 10 Tagen

TagThemaStatus
1OOP-Konzepte & erste Klasse✅ Abgeschlossen
2Attribute & Methoden✅ Abgeschlossen
3Datenkapselung & Sichtbarkeit✅ Abgeschlossen
→ 4Konstruktoren📍 Du bist hier
5Konstanten & Static🔴 KOPFNUSS
6Vererbung – Grundlagen
7Vererbung – Polymorphie & Abstrakte Klassen
8Typumwandlung & instanceof
9Interfaces & Enumerationen
10Ausnahmebehandlung🔴 KOPFNUSS

Voraussetzung: Tag 1–3 abgeschlossen
Folgekurs: Erweiterte Techniken (Kurs 3)


🔄 Kurz-Wiederholung: Challenge von Tag 3

Die Aufgabe war: Erstelle eine gekapselte Bankkonto-Klasse mit Validierung.

Lösung:

public class Bankkonto {
    private final String kontonummer;  // Nur Getter, kein Setter!
    private double kontostand;
    private String pin;
    
    // Konstruktor (wird heute erklärt!)
    public Bankkonto(String kontonummer, String pin) {
        if (kontonummer == null || kontonummer.isBlank()) {
            throw new IllegalArgumentException("Kontonummer darf nicht leer sein!");
        }
        if (!pin.matches("\\d{4}")) {
            throw new IllegalArgumentException("PIN muss genau 4 Ziffern haben!");
        }
        this.kontonummer = kontonummer;
        this.pin = pin;
        this.kontostand = 0;
    }
    
    // Getter
    public String getKontonummer() {
        return kontonummer;
    }
    
    public double getKontostand() {
        return kontostand;
    }
    
    // Kein Getter für PIN! (Sicherheit)
    
    // Einzahlen
    public void einzahlen(double betrag) {
        if (betrag <= 0) {
            throw new IllegalArgumentException("Betrag muss positiv sein!");
        }
        kontostand += betrag;
        System.out.println("✓ Eingezahlt: " + betrag + " € | Kontostand: " + kontostand + " €");
    }
    
    // Abheben mit PIN-Prüfung
    public void abheben(double betrag, String pinEingabe) {
        if (!pin.equals(pinEingabe)) {
            throw new SecurityException("Falsche PIN!");
        }
        if (betrag <= 0) {
            throw new IllegalArgumentException("Betrag muss positiv sein!");
        }
        if (betrag > kontostand) {
            throw new IllegalArgumentException("Nicht genug Guthaben!");
        }
        kontostand -= betrag;
        System.out.println("✓ Abgehoben: " + betrag + " € | Kontostand: " + kontostand + " €");
    }
    
    // PIN ändern
    public void pinAendern(String altePin, String neuePin) {
        if (!pin.equals(altePin)) {
            throw new SecurityException("Alte PIN ist falsch!");
        }
        if (!neuePin.matches("\\d{4}")) {
            throw new IllegalArgumentException("Neue PIN muss genau 4 Ziffern haben!");
        }
        this.pin = neuePin;
        System.out.println("✓ PIN wurde geändert.");
    }
}

Hast du’s? Der Konstruktor hat dir vielleicht Kopfzerbrechen bereitet — genau deshalb lernen wir das heute!


⚡ Das Wichtigste in 30 Sekunden

Dein Problem: new Auto() erzeugt ein Auto ohne Farbe, ohne PS, ohne alles. Du musst dann manuell alle Setter aufrufen.

Die Lösung: Konstruktoren initialisieren Objekte direkt beim Erstellen — vollständig und validiert.

Heute lernst du:

  • ✅ Was Konstruktoren sind und wie sie funktionieren
  • ✅ Default-Konstruktor vs. eigene Konstruktoren
  • ✅ Konstruktorüberladung — mehrere Wege ein Objekt zu erzeugen
  • this() — Konstruktoren verketten

Für wen ist dieser Artikel?

  • 🌱 OOP-Neulinge: Du lernst Konstruktoren von Grund auf
  • 🌿 OOP-Umsteiger: Du siehst Java-Besonderheiten (vs. C#, C++)
  • 🌳 Profis: Im Bonus: Builder-Pattern, Copy-Konstruktor, Factory Methods

Zeit-Investment: 30–35 Minuten


👋 Nova: „Endlich richtig initialisieren!“

Hey! 👋

Nova hier.

Erinnerst du dich an unsere Auto-Klasse? Bisher haben wir das gemacht:

Auto auto = new Auto();
auto.setFarbe("Rot");
auto.setPs(150);
auto.setMarke("VW");

Drei Zeilen nur um ein Auto zu konfigurieren. Und was, wenn du setFarbe() vergisst? Dann hast du ein Auto ohne Farbe. Nicht gut.

Konstruktoren lösen das Problem. Du sagst: „Hey, ein Auto BRAUCHT Farbe und PS. Ohne geht’s nicht!“ Und Java zwingt den Nutzer, diese Werte beim Erstellen anzugeben.

Real talk: Als ich das erste Mal this() gesehen habe — also einen Konstruktor, der einen anderen aufruft — war ich kurz verwirrt. „Häh, ein Konstruktor ruft sich selbst auf?!“ Aber nein, er ruft einen ANDEREN Konstruktor der gleichen Klasse auf. Heute erkläre ich dir das so, dass es klickt.

Los geht’s! 🚀


🟢 GRUNDLAGEN

Was ist ein Konstruktor?

Ein Konstruktor ist eine spezielle Methode, die beim Erstellen eines Objekts aufgerufen wird.

Auto auto = new Auto("Rot", 150);
//               ↑
//          Konstruktor wird aufgerufen!

Eigenschaften eines Konstruktors:

  • Hat keinen Rückgabetyp (nicht mal void!)
  • Heißt exakt wie die Klasse
  • Wird automatisch bei new aufgerufen
  • Kann Parameter haben

Abbildung 1: Ein Konstruktor hat keinen Rückgabetyp und heißt wie die Klasse.

Dein erster Konstruktor

public class Auto {
    private String farbe;
    private int ps;
    
    // KONSTRUKTOR
    public Auto(String farbe, int ps) {
        this.farbe = farbe;
        this.ps = ps;
    }
    
    public void info() {
        System.out.println(farbe + " Auto mit " + ps + " PS");
    }
}

Verwendung:

Auto auto = new Auto("Rot", 150);
auto.info();  // Rot Auto mit 150 PS

Was passiert bei new Auto("Rot", 150)?

  1. Java reserviert Speicher für ein neues Auto-Objekt
  2. Der Konstruktor Auto(String farbe, int ps) wird aufgerufen
  3. Die Attribute werden initialisiert
  4. Das fertige Objekt wird zurückgegeben

Der Default-Konstruktor

Wenn du keinen Konstruktor schreibst, erstellt Java automatisch einen Default-Konstruktor:

public class Auto {
    private String farbe;
    private int ps;
    // Kein Konstruktor geschrieben!
}

// Java generiert heimlich:
// public Auto() { }

Aber Achtung:

Konstruktoren

Abbildung 2: Sobald du EINEN eigenen Konstruktor schreibst, generiert Java keinen Default-Konstruktor mehr!

public class Auto {
    private String farbe;
    private int ps;
    
    public Auto(String farbe, int ps) {  // Eigener Konstruktor
        this.farbe = farbe;
        this.ps = ps;
    }
}

Auto auto = new Auto();  // ❌ FEHLER! Kein Default-Konstruktor vorhanden!

Lösung: Wenn du new Auto() UND new Auto("Rot", 150) brauchst, schreibe beide Konstruktoren:

public Auto() {
    this.farbe = "Weiß";  // Standardwerte
    this.ps = 100;
}

public Auto(String farbe, int ps) {
    this.farbe = farbe;
    this.ps = ps;
}

Konstruktorüberladung

Wie bei Methoden kannst du mehrere Konstruktoren mit unterschiedlichen Parametern haben:

public class Auto {
    private String farbe;
    private int ps;
    private String marke;
    
    // Konstruktor 1: Alle Parameter
    public Auto(String farbe, int ps, String marke) {
        this.farbe = farbe;
        this.ps = ps;
        this.marke = marke;
    }
    
    // Konstruktor 2: Nur Farbe und PS
    public Auto(String farbe, int ps) {
        this.farbe = farbe;
        this.ps = ps;
        this.marke = "Unbekannt";
    }
    
    // Konstruktor 3: Keine Parameter (Default-Werte)
    public Auto() {
        this.farbe = "Weiß";
        this.ps = 100;
        this.marke = "Standard";
    }
}

Verwendung:

Auto auto1 = new Auto("Rot", 150, "VW");     // Alle Werte
Auto auto2 = new Auto("Blau", 200);          // Marke = "Unbekannt"
Auto auto3 = new Auto();                      // Alle Defaults

Konstruktoren verketten mit this()

Statt Code zu wiederholen, kann ein Konstruktor einen anderen aufrufen:

Abbildung 3: Mit this() ruft ein Konstruktor einen anderen auf — Code-Wiederverwendung!

public class Auto {
    private String farbe;
    private int ps;
    private String marke;
    
    // HAUPTKONSTRUKTOR — hier passiert die echte Arbeit
    public Auto(String farbe, int ps, String marke) {
        // Validierung
        if (farbe == null || farbe.isBlank()) {
            throw new IllegalArgumentException("Farbe darf nicht leer sein!");
        }
        if (ps < 1 || ps > 2000) {
            throw new IllegalArgumentException("PS muss zwischen 1 und 2000 liegen!");
        }
        
        this.farbe = farbe;
        this.ps = ps;
        this.marke = marke;
        
        System.out.println("Auto erstellt: " + farbe + ", " + ps + " PS, " + marke);
    }
    
    // Konstruktor 2 — ruft Hauptkonstruktor auf
    public Auto(String farbe, int ps) {
        this(farbe, ps, "Unbekannt");  // → ruft Auto(farbe, ps, marke)
    }
    
    // Konstruktor 3 — ruft Konstruktor 2 auf
    public Auto() {
        this("Weiß", 100);  // → ruft Auto(farbe, ps)
    }
}

Vorteile:

  • Validierung nur an EINER Stelle (Hauptkonstruktor)
  • Weniger Code-Duplikation
  • Änderungen nur an einer Stelle nötig

Regeln für this():

  • Muss die erste Anweisung im Konstruktor sein
  • Nur ein this()-Aufruf pro Konstruktor
  • Keine Zyklen (A→B→A ist verboten!)

Validierung im Konstruktor

Konstruktoren sind der perfekte Ort für Validierung:

public class Bankkonto {
    private final String kontonummer;
    private double kontostand;
    
    public Bankkonto(String kontonummer, double startguthaben) {
        // Validierung der Kontonummer
        if (kontonummer == null || kontonummer.isBlank()) {
            throw new IllegalArgumentException("Kontonummer darf nicht leer sein!");
        }
        if (!kontonummer.matches("DE\\d{20}")) {
            throw new IllegalArgumentException("Ungültiges IBAN-Format!");
        }
        
        // Validierung des Startguthabens
        if (startguthaben < 0) {
            throw new IllegalArgumentException("Startguthaben darf nicht negativ sein!");
        }
        
        this.kontonummer = kontonummer;
        this.kontostand = startguthaben;
    }
}

Jetzt kann kein ungültiges Bankkonto erstellt werden!

Bankkonto k1 = new Bankkonto("DE89370400440532013000", 1000);  // ✅ OK
Bankkonto k2 = new Bankkonto("", 500);  // ❌ Exception: Kontonummer leer!
Bankkonto k3 = new Bankkonto("DE123", -100);  // ❌ Exception: Ungültiges Format!

final-Attribute im Konstruktor

Attribute, die mit final markiert sind, können nur im Konstruktor gesetzt werden:

public class Person {
    private final String name;  // Kann nach Erstellung nicht mehr geändert werden!
    private int alter;
    
    public Person(String name, int alter) {
        this.name = name;   // ✅ Erlaubt im Konstruktor
        this.alter = alter;
    }
    
    public void aelterWerden() {
        this.alter++;       // ✅ Erlaubt (nicht final)
        // this.name = "Neu";  ❌ FEHLER! name ist final
    }
    
    // Kein setName() möglich! Nur Getter:
    public String getName() {
        return name;
    }
}

Wann final verwenden?

  • Für Werte, die sich nie ändern sollten (ID, Kontonummer, Geburtsdatum)
  • Für Immutable Objects (unveränderliche Objekte)

🟡 PROFESSIONALS

Schon OOP-Erfahrung aus C#, C++, PHP? Hier sind die Java-Besonderheiten.

Für C#-Umsteiger: Kein : this()

In C# verketten Konstruktoren mit : this() nach der Signatur:

// C#
public Auto(string farbe) : this(farbe, 100) { }

In Java steht this() im Body als erste Anweisung:

// Java
public Auto(String farbe) {
    this(farbe, 100);  // Muss erste Zeile sein!
}

Für C++-Umsteiger: Keine Initializer Lists

C++ hat Initializer Lists:

// C++
Auto::Auto(string farbe, int ps) : farbe(farbe), ps(ps) { }

Java hat das nicht — alles passiert im Body:

// Java
public Auto(String farbe, int ps) {
    this.farbe = farbe;
    this.ps = ps;
}

Aber: Java hat seit Java 16 Records, die ähnlich kompakt sind:

public record Auto(String farbe, int ps) {}
// Konstruktor wird automatisch generiert!

Für PHP-Umsteiger: Kein __construct

PHP verwendet __construct:

// PHP
class Auto {
    public function __construct($farbe, $ps) {
        $this->farbe = $farbe;
        $this->ps = $ps;
    }
}

Java verwendet den Klassennamen:

// Java
public class Auto {
    public Auto(String farbe, int ps) {
        this.farbe = farbe;
        this.ps = ps;
    }
}

Lombok: @AllArgsConstructor & Co.

Mit Lombok werden Konstruktoren trivial:

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;

@NoArgsConstructor        // Generiert: public Auto() {}
@AllArgsConstructor       // Generiert: public Auto(farbe, ps, marke) {}
public class Auto {
    private String farbe;
    private int ps;
    private String marke;
}

@RequiredArgsConstructor — Nur für final-Felder:

@RequiredArgsConstructor
public class Auto {
    private final String farbe;  // Im Konstruktor
    private final int ps;        // Im Konstruktor
    private String marke;        // Nicht im Konstruktor
}
// Generiert: public Auto(String farbe, int ps) {}

🔵 BONUS

Für Wissbegierige: Builder-Pattern, Copy-Konstruktor, Factory Methods.

Copy-Konstruktor

Ein Konstruktor, der ein anderes Objekt kopiert:

public class Auto {
    private String farbe;
    private int ps;
    
    // Normaler Konstruktor
    public Auto(String farbe, int ps) {
        this.farbe = farbe;
        this.ps = ps;
    }
    
    // COPY-KONSTRUKTOR
    public Auto(Auto original) {
        this.farbe = original.farbe;
        this.ps = original.ps;
    }
}
Auto original = new Auto("Rot", 150);
Auto kopie = new Auto(original);  // Kopie erstellen

Factory Methods statt Konstruktoren

Manchmal sind statische Factory-Methoden besser als Konstruktoren:

public class Punkt {
    private final double x;
    private final double y;
    
    private Punkt(double x, double y) {  // Private!
        this.x = x;
        this.y = y;
    }
    
    // Factory Methods mit sprechenden Namen
    public static Punkt kartesisch(double x, double y) {
        return new Punkt(x, y);
    }
    
    public static Punkt polar(double radius, double winkel) {
        double x = radius * Math.cos(winkel);
        double y = radius * Math.sin(winkel);
        return new Punkt(x, y);
    }
    
    public static Punkt ursprung() {
        return new Punkt(0, 0);
    }
}
Punkt p1 = Punkt.kartesisch(3, 4);
Punkt p2 = Punkt.polar(5, Math.PI / 4);
Punkt p3 = Punkt.ursprung();

Vorteile:

  • Sprechende Namen (vs. nur Konstruktor-Überladung)
  • Können gecacht werden
  • Können Subtypen zurückgeben

Builder-Pattern

Für Klassen mit vielen optionalen Parametern:

public class Auto {
    private final String farbe;
    private final int ps;
    private final String marke;
    private final boolean klimaanlage;
    private final boolean schiebedach;
    
    private Auto(Builder builder) {
        this.farbe = builder.farbe;
        this.ps = builder.ps;
        this.marke = builder.marke;
        this.klimaanlage = builder.klimaanlage;
        this.schiebedach = builder.schiebedach;
    }
    
    public static class Builder {
        // Pflicht
        private final String farbe;
        private final int ps;
        // Optional mit Defaults
        private String marke = "Unbekannt";
        private boolean klimaanlage = false;
        private boolean schiebedach = false;
        
        public Builder(String farbe, int ps) {
            this.farbe = farbe;
            this.ps = ps;
        }
        
        public Builder marke(String marke) {
            this.marke = marke;
            return this;
        }
        
        public Builder mitKlimaanlage() {
            this.klimaanlage = true;
            return this;
        }
        
        public Builder mitSchiebedach() {
            this.schiebedach = true;
            return this;
        }
        
        public Auto build() {
            return new Auto(this);
        }
    }
}
Auto auto = new Auto.Builder("Rot", 150)
    .marke("VW")
    .mitKlimaanlage()
    .mitSchiebedach()
    .build();

Mit Lombok viel einfacher:

@Builder
public class Auto {
    private String farbe;
    private int ps;
    @Builder.Default private String marke = "Unbekannt";
    private boolean klimaanlage;
    private boolean schiebedach;
}

Auto auto = Auto.builder()
    .farbe("Rot")
    .ps(150)
    .klimaanlage(true)
    .build();

💬 Real Talk

Tom fragt, Nova erklärt — Die Fragen, die sich jeder stellt.

Java Fleet Pausenraum, 15:30 Uhr. Tom hat seinen Laptop dabei, Nova macht sich einen Tee.


Tom: Nova, ich check nicht, wann ich this() und wann ich this.attribut verwende. Das sieht beides gleich aus!

Nova: lacht Gute Frage! Pass auf:

Nova: this.farbe ist das Attribut — du greifst auf eine Variable des aktuellen Objekts zu.

Nova: this("Rot", 150) ist ein Konstruktor-Aufruf — du rufst einen anderen Konstruktor der gleichen Klasse auf.

Tom: Also Klammern = Konstruktor?

Nova: Exakt! Ohne Klammern = Attribut oder Methode. Mit Klammern und passenden Parametern = anderer Konstruktor.

Tom: Okay… und warum überhaupt verketten? Ich könnte doch einfach in jedem Konstruktor alles nochmal schreiben?

Nova: Könntest du. Aber stell dir vor, du hast Validierung. „PS muss zwischen 1 und 2000 liegen.“ Wenn das in drei Konstruktoren steht und du’s ändern musst…

Tom: …muss ich’s dreimal ändern. Und vergesse garantiert einen.

Nova: nickt Genau. Mit Verkettung hast du die Logik nur im Hauptkonstruktor. Alle anderen rufen den auf. Eine Stelle, fertig.

Tom: Das ergibt Sinn! Letzte Frage: Wann nehme ich einen Konstruktor und wann Setter?

Nova: trinkt Tee Faustregel: Pflichtfelder im Konstruktor, optionale Felder über Setter. Ein Auto BRAUCHT eine Farbe — also Konstruktor. Die Klimaanlage ist optional — Setter oder Builder.

Tom: Und final-Attribute?

Nova: Die MÜSSEN im Konstruktor gesetzt werden. Danach geht nichts mehr. Perfekt für IDs, Kontonummern, sowas.

Tom: Cool. Danke, Nova!


✅ Checkpoint: Hast du es verstanden?

Quiz

Frage 1: Was ist der Unterschied zwischen einem Konstruktor und einer normalen Methode?

Frage 2: Wann generiert Java einen Default-Konstruktor automatisch?

Frage 3: Was bedeutet this("Rot", 150) in einem Konstruktor?

Frage 4: Wo muss ein this()-Aufruf stehen?

Frage 5: Du siehst diesen Code:

public class Tier {
    public Tier(String name) { }
}

Tier t = new Tier();

Was passiert und warum?


Mini-Challenge

Aufgabe: Erstelle eine Mitarbeiter-Klasse mit Konstruktor-Verkettung:

  1. Attribute:
    • personalnummer (final String)
    • name (String)
    • abteilung (String)
    • gehalt (double)
  2. Konstruktoren:
    • Mitarbeiter(String personalnummer, String name, String abteilung, double gehalt) — Hauptkonstruktor mit Validierung
    • Mitarbeiter(String personalnummer, String name, String abteilung) — Gehalt = 3000€
    • Mitarbeiter(String personalnummer, String name) — Abteilung = „Allgemein“, Gehalt = 3000€
  3. Validierung:
    • Personalnummer: Muss mit „P“ beginnen, gefolgt von 4 Ziffern (z.B. „P1234“)
    • Name: Darf nicht leer sein
    • Gehalt: Muss mindestens 1500€ sein

Lösung: Findest du am Anfang von Tag 5! 🚀


❓ FAQ — Häufig gestellte Fragen

Frage 1: Kann ein Konstruktor etwas zurückgeben?

Nein! Konstruktoren haben keinen Rückgabetyp — nicht mal void. Sie geben implizit das neue Objekt zurück.


Frage 2: Kann ich einen Konstruktor private machen?

Ja! Das ist nützlich für:

  • Singleton-Pattern (nur eine Instanz)
  • Factory Methods (Objekte nur über statische Methoden erstellen)
  • Utility-Klassen (nur statische Methoden, keine Instanzen)
public class Singleton {
    private static Singleton instance;
    
    private Singleton() { }  // Private!
    
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

Frage 3: Kann ich return in einem Konstruktor verwenden?

Ja, aber nur return; (ohne Wert) zum vorzeitigen Beenden:

public Auto(String farbe) {
    if (farbe == null) {
        System.out.println("Warnung: Keine Farbe angegeben!");
        return;  // Konstruktor beenden
    }
    this.farbe = farbe;
}

Aber besser: Exception werfen statt stillschweigend abbrechen!


Frage 4: Was ist der Unterschied zwischen this() und super()?

  • this() ruft einen anderen Konstruktor derselben Klasse auf
  • super() ruft einen Konstruktor der Elternklasse auf (Vererbung → Tag 6)

Frage 5: Wie viele Konstruktoren sollte eine Klasse haben?

So viele wie sinnvoll! Aber:

  • Bei mehr als 3-4 Konstruktoren → denk über Builder-Pattern nach
  • Verwende Konstruktor-Verkettung um Duplikation zu vermeiden

Frage 6: Kann ein Konstruktor eine Exception werfen?

Ja! Das ist sogar Best Practice für Validierung:

public Auto(int ps) throws IllegalArgumentException {
    if (ps < 0) {
        throw new IllegalArgumentException("PS darf nicht negativ sein!");
    }
    this.ps = ps;
}

Frage 7: Bernd meinte mal, Konstruktoren mit mehr als 3 Parametern seien „ein Zeichen schlechten Designs“. Stimmt das?

schmunzelt Typisch Bernd. Aber er hat recht — viele Parameter sind ein Code Smell. Alternativen:

  • Builder-Pattern für viele optionale Parameter
  • Parameter-Objekt (eine Klasse, die alle Parameter bündelt)
  • Factory Methods für verschiedene Erstellungswege

Real talk: Wenn dein Konstruktor 10 Parameter hat, ist deine Klasse wahrscheinlich zu groß. Bernd würde sagen: „Aufteilen.“ Und er hätte recht. 🤷


📚 Quiz-Lösungen

Frage 1: Was ist der Unterschied zwischen einem Konstruktor und einer normalen Methode?

Antwort:

KonstruktorMethode
Kein RückgabetypHat Rückgabetyp (oder void)
Name = KlassennameBeliebiger Name
Wird bei new aufgerufenWird explizit aufgerufen
Initialisiert das ObjektFührt Aktionen aus

Frage 2: Wann generiert Java einen Default-Konstruktor automatisch?

Antwort:

Java generiert einen Default-Konstruktor (public Klasse() {}) nur wenn du KEINEN eigenen Konstruktor schreibst.

Sobald du einen eigenen Konstruktor definierst, musst du den Default-Konstruktor selbst schreiben, falls du ihn brauchst.


Frage 3: Was bedeutet this("Rot", 150) in einem Konstruktor?

Antwort:

this("Rot", 150) ruft einen anderen Konstruktor der gleichen Klasse auf, der zwei Parameter (String und int) erwartet.

Es ist Konstruktor-Verkettung — dieser Konstruktor delegiert die Arbeit an einen anderen.


Frage 4: Wo muss ein this()-Aufruf stehen?

Antwort:

Ein this()-Aufruf muss die erste Anweisung im Konstruktor sein.

public Auto() {
    System.out.println("Hallo");  // ❌ FEHLER!
    this("Rot", 150);
}

public Auto() {
    this("Rot", 150);             // ✅ Erste Zeile!
    System.out.println("Hallo");
}

Frage 5: Was passiert und warum?

public class Tier {
    public Tier(String name) { }
}

Tier t = new Tier();  // ❌ FEHLER!

Antwort:

Compilerfehler: „constructor Tier() is undefined“

Da ein eigener Konstruktor Tier(String name) existiert, generiert Java keinen Default-Konstruktor mehr. new Tier() funktioniert nicht.

Lösung: Einen Default-Konstruktor hinzufügen:

public Tier() {
    this("Unbekannt");
}

🎉 Tag 4 geschafft!

Du hast es geschafft! 🚀

Das hast du heute gelernt:

  • ✅ Konstruktoren initialisieren Objekte bei new
  • ✅ Default-Konstruktor vs. eigene Konstruktoren
  • ✅ Konstruktor-Überladung für flexible Objekterstellung
  • this() für Konstruktor-Verkettung
  • ✅ Validierung gehört in den Konstruktor!

Ab jetzt: Deine Objekte werden von Anfang an richtig initialisiert — vollständig und validiert! 💪


🔮 Wie geht’s weiter?

Morgen in Tag 5: Konstanten & Static 🔴 KOPFNUSS

Das wird anspruchsvoller! Du lernst:

  • static — Klassenvariablen und -methoden
  • final — Konstanten
  • Wann static und wann nicht?
  • static final — echte Konstanten

Achtung: Tag 5 ist als KOPFNUSS markiert. Nimm dir Zeit! 🧠


📦 Downloads

Starter-Projekt für Tag 4:

⬇️ Tag04_Konstruktoren.zip — Komplettes Maven-Projekt

Inhalt:

Tag04_Konstruktoren/
├── pom.xml
├── README.md
└── src/main/java/
    └── de/javafleet/oop/
        ├── Main.java
        └── model/
            ├── Auto.java           (mit Konstruktor-Verkettung)
            └── Bankkonto.java      (Lösung Tag 3 Challenge)

Quick Start:

cd Tag04_Konstruktoren
mvn compile
mvn exec:java

🔧 Troubleshooting

Problem: „constructor Xxx() is undefined“

error: constructor Auto() is undefined
    Auto auto = new Auto();

Lösung: Du hast einen eigenen Konstruktor, aber keinen Default-Konstruktor. Füge einen hinzu oder verwende den passenden Konstruktor.


Problem: „call to this must be first statement“

error: call to this must be first statement in constructor
    System.out.println("Hi");
    this("Rot");  // Zu spät!

Lösung: this() muss die erste Zeile sein:

public Auto() {
    this("Rot");  // ✅ Erste Zeile!
    System.out.println("Hi");
}

Problem: „cannot assign a value to final variable“

error: cannot assign a value to final variable name
    this.name = "Neu";

Lösung: final-Variablen können nur im Konstruktor gesetzt werden. Danach sind sie unveränderlich.


🔗 Resources & Links

🟢 Für Einsteiger

RessourceBeschreibungSprache
Oracle — ConstructorsOffizielle Doku🇬🇧
W3Schools — Java ConstructorsEinfache Erklärungen🇬🇧
Java ist auch eine Insel — KonstruktorenKostenloses Buch🇩🇪

🟡 Für Fortgeschrittene

RessourceBeschreibungSprache
Baeldung — Java ConstructorsTiefgehende Erklärung🇬🇧
Effective Java — Item 1Static Factory Methods🇬🇧
Lombok @BuilderBuilder-Pattern automatisch🇬🇧

🛠️ Tools

ToolBeschreibungLink
Lombok@AllArgsConstructor, @Builderprojectlombok.org
IntelliJ GeneratorAlt+Insert → ConstructorEingebaut

💬 Feedback

Fragen? Schreib mir:

  • Nova: nova.trent@java-developer.online

Feedback zum Kurs? Nutze den 👎-Button unten!


👋 Bis morgen!

Tag 4 ist durch. Deine Objekte werden jetzt von Anfang an richtig initialisiert!

Morgen wird’s anspruchsvoller: Static & Konstanten. Das ist ein KOPFNUSS-Tag — nimm dir Zeit!

See you! 🚀


Nova Trent
Junior Entwicklerin bei Java Fleet Systems Consulting
„Konstruktoren sind wie Geburtshelfer für Objekte!“ 👶


Tags: #Java #OOP #Konstruktoren #this #Initialisierung #Tutorial

© 2025 Java Fleet Systems Consulting | java-developer.online

Autor

  • Ensign Nova Trent

    24 Jahre alt, frisch von der Universität als Junior Entwicklerin bei Java Fleet Systems Consulting. Nova ist brilliant in Algorithmen und Datenstrukturen, aber neu in der praktischen Java-Enterprise-Entwicklung. Sie brennt darauf, ihre ersten echten Projekte zu bauen und entdeckt dabei die Lücke zwischen Uni-Theorie und Entwickler-Realität. Sie liebt Star Treck das ist der Grund warum alle Sie Ensign Nova nennen und arbeitet daraufhin das sie Ihren ersten Knopf am Kragen bekommt.