Zum Inhalt springen
Java Anfänger 25 min

Konstruktoren

Alles über Konstruktoren in Java: Default-Konstruktor, parametrisierte Konstruktoren, Konstruktor-Überladung und this()-Aufrufe.

Aktualisiert:

Konstruktoren in Java

Im letzten Kapitel hast du Objekte mit new erstellt und dann die Felder einzeln gesetzt. Das ist umstaendlich und fehleranfaellig! Konstruktoren loesen dieses Problem: Sie initialisieren ein Objekt direkt bei der Erstellung.

Was ist ein Konstruktor?

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

public class Hund {
    String name;
    String rasse;
    int alter;

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

// Objekt erstellen -- Konstruktor wird aufgerufen
var rex = new Hund("Rex", "Schaeferhund", 5);

Regeln fuer Konstruktoren

RegelErlaeuterung
Name = KlassennameHund() fuer Klasse Hund
Kein RueckgabetypNicht mal void!
Wird mit new aufgerufennew Hund("Rex", "Schaeferhund", 5)
Kann ueberladen werdenMehrere Konstruktoren mit verschiedenen Parametern

Ohne Konstruktor: Der Default-Konstruktor

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

public class Katze {
    String name;
    int alter;
    // Java erstellt automatisch: Katze() { }
}

var katze = new Katze(); // Default-Konstruktor
katze.name = "Mia";      // Felder einzeln setzen -- umstaendlich!
katze.alter = 3;

Achtung: Sobald du einen eigenen Konstruktor schreibst, gibt es den Default-Konstruktor nicht mehr:

public class Vogel {
    String name;

    Vogel(String name) {
        this.name = name;
    }
}

// var v = new Vogel(); // FEHLER! Kein Default-Konstruktor mehr!
var v = new Vogel("Tweety"); // OK

Konstruktor-Ueberladung

Du kannst mehrere Konstruktoren mit unterschiedlichen Parametern haben:

public class Auto {
    String marke;
    String modell;
    int baujahr;
    String farbe;

    // Konstruktor mit allen Feldern
    Auto(String marke, String modell, int baujahr, String farbe) {
        this.marke = marke;
        this.modell = modell;
        this.baujahr = baujahr;
        this.farbe = farbe;
    }

    // Konstruktor ohne Farbe (Standardfarbe)
    Auto(String marke, String modell, int baujahr) {
        this(marke, modell, baujahr, "Schwarz"); // Ruft anderen Konstruktor auf
    }

    // Konstruktor nur mit Marke und Modell
    Auto(String marke, String modell) {
        this(marke, modell, 2026); // Ruft den Konstruktor mit 3 Parametern auf
    }

    void info() {
        System.out.printf("%s %s (%d) in %s%n", marke, modell, baujahr, farbe);
    }
}
var auto1 = new Auto("BMW", "3er", 2024, "Weiss");
var auto2 = new Auto("VW", "Golf", 2023);          // Farbe = Schwarz
var auto3 = new Auto("Audi", "A4");                  // Baujahr = 2026, Farbe = Schwarz

auto1.info(); // BMW 3er (2024) in Weiss
auto2.info(); // VW Golf (2023) in Schwarz
auto3.info(); // Audi A4 (2026) in Schwarz

this() — Konstruktoren verketten

Mit this() rufst du einen anderen Konstruktor derselben Klasse auf:

public class Rechteck {
    double breite;
    double hoehe;

    // Haupt-Konstruktor
    Rechteck(double breite, double hoehe) {
        this.breite = breite;
        this.hoehe = hoehe;
    }

    // Quadrat-Konstruktor
    Rechteck(double seite) {
        this(seite, seite); // Ruft den Haupt-Konstruktor auf
    }

    // Standard-Konstruktor
    Rechteck() {
        this(1.0, 1.0); // Einheitsquadrat
    }

    double flaeche() {
        return breite * hoehe;
    }
}

Regel: this() muss die erste Anweisung im Konstruktor sein!

// FEHLER:
Rechteck(double seite) {
    System.out.println("Erstelle Quadrat"); // FEHLER! this() muss zuerst kommen
    this(seite, seite);
}

// RICHTIG:
Rechteck(double seite) {
    this(seite, seite);
    System.out.println("Quadrat erstellt"); // OK nach this()
}

Validierung im Konstruktor

Der Konstruktor ist der perfekte Ort, um Eingaben zu pruefen:

public class Temperatur {
    private double celsius;

    Temperatur(double celsius) {
        if (celsius < -273.15) {
            throw new IllegalArgumentException(
                "Temperatur kann nicht unter -273.15°C liegen!"
            );
        }
        this.celsius = celsius;
    }

    double inFahrenheit() {
        return celsius * 9.0 / 5.0 + 32;
    }

    double inKelvin() {
        return celsius + 273.15;
    }

    @Override
    public String toString() {
        return String.format("%.1f°C", celsius);
    }
}
var temp = new Temperatur(36.6);
System.out.println(temp);                  // 36.6°C
System.out.println(temp.inFahrenheit());   // 97.88
System.out.println(temp.inKelvin());       // 309.75

// var falsch = new Temperatur(-500); // IllegalArgumentException!

Compact Constructors in Records

Records haben einen speziellen kompakten Konstruktor fuer Validierung:

record Alter(int wert) {
    // Kompakter Konstruktor -- ohne Parameterliste!
    Alter {
        if (wert < 0 || wert > 150) {
            throw new IllegalArgumentException("Ungueltiges Alter: " + wert);
        }
    }
}

var alter = new Alter(25);       // OK
System.out.println(alter.wert()); // 25
// var ungueltig = new Alter(-5); // IllegalArgumentException!
record Email(String adresse) {
    Email {
        if (adresse == null || !adresse.contains("@")) {
            throw new IllegalArgumentException("Ungueltige E-Mail: " + adresse);
        }
        adresse = adresse.toLowerCase().strip(); // Normalisierung
    }
}

var email = new Email("  MAX@Example.COM  ");
System.out.println(email.adresse()); // max@example.com

Copy-Konstruktor

Ein Konstruktor, der ein bestehendes Objekt kopiert:

public class Punkt {
    double x;
    double y;

    Punkt(double x, double y) {
        this.x = x;
        this.y = y;
    }

    // Copy-Konstruktor
    Punkt(Punkt anderer) {
        this(anderer.x, anderer.y);
    }

    @Override
    public String toString() {
        return "(" + x + ", " + y + ")";
    }
}

var original = new Punkt(3, 4);
var kopie = new Punkt(original);
kopie.x = 10;

System.out.println(original); // (3.0, 4.0) -- unveraendert
System.out.println(kopie);    // (10.0, 4.0) -- eigene Kopie

Vergleich mit Python

# Python: __init__
class Hund:
    def __init__(self, name, rasse, alter=0):
        self.name = name
        self.rasse = rasse
        self.alter = alter

rex = Hund("Rex", "Schaeferhund", 5)
bella = Hund("Bella", "Pudel")  # alter = 0 (Standardwert)
// Java: Konstruktor-Ueberladung
public class Hund {
    String name;
    String rasse;
    int alter;

    Hund(String name, String rasse, int alter) {
        this.name = name;
        this.rasse = rasse;
        this.alter = alter;
    }

    Hund(String name, String rasse) {
        this(name, rasse, 0); // Standardwert fuer alter
    }
}

Python nutzt Standardwerte in Parametern, Java nutzt Konstruktor-Ueberladung.

Praktisches Beispiel: Spieler-Klasse

public class Spieler {
    private String name;
    private int level;
    private int erfahrung;
    private int maxHP;
    private int aktuelleHP;

    // Neuer Spieler
    Spieler(String name) {
        this(name, 1, 0);
    }

    // Spieler laden (aus Speicherstand)
    Spieler(String name, int level, int erfahrung) {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("Name darf nicht leer sein!");
        }
        if (level < 1) {
            throw new IllegalArgumentException("Level muss mindestens 1 sein!");
        }

        this.name = name;
        this.level = level;
        this.erfahrung = erfahrung;
        this.maxHP = 100 + (level - 1) * 20;
        this.aktuelleHP = maxHP;
    }

    void schadenNehmen(int schaden) {
        aktuelleHP = Math.max(0, aktuelleHP - schaden);
        if (aktuelleHP == 0) {
            System.out.println(name + " ist besiegt!");
        }
    }

    void heilen(int menge) {
        aktuelleHP = Math.min(maxHP, aktuelleHP + menge);
    }

    @Override
    public String toString() {
        return "%s (Level %d) - HP: %d/%d".formatted(name, level, aktuelleHP, maxHP);
    }
}
var held = new Spieler("Arthas");
System.out.println(held); // Arthas (Level 1) - HP: 100/100

held.schadenNehmen(30);
System.out.println(held); // Arthas (Level 1) - HP: 70/100

held.heilen(15);
System.out.println(held); // Arthas (Level 1) - HP: 85/100

Uebungen

Uebung 1: Kreis-Klasse

Erstelle eine Klasse Kreis mit einem Konstruktor, der den Radius entgegennimmt. Validiere, dass der Radius positiv ist. Fuege Methoden fuer Flaeche und Umfang hinzu.

Uebung 2: Buch-Konstruktoren

Erstelle eine Klasse Buch mit drei ueberladenen Konstruktoren:

  • Buch(String titel) — Autor = “Unbekannt”, Seiten = 0
  • Buch(String titel, String autor) — Seiten = 0
  • Buch(String titel, String autor, int seiten) — Alle Felder

Uebung 3: Bankkonto

Erstelle eine Klasse Bankkonto mit Validierung im Konstruktor (Inhaber darf nicht leer sein, Startguthaben >= 0). Fuege Methoden fuer Einzahlung und Abhebung hinzu.

Uebung 4: Record mit Validierung

Erstelle einen Record Passwort(String wert) mit Validierung: mindestens 8 Zeichen, mindestens eine Ziffer.

Was kommt als Naechstes?

In der naechsten Lektion lernst du Vererbung — wie Klassen Eigenschaften und Verhalten an andere Klassen weitergeben koennen.

Zusammenfassung

  • Konstruktoren initialisieren Objekte bei der Erstellung mit new
  • Sie haben den gleichen Namen wie die Klasse und keinen Rueckgabetyp
  • Ohne eigenen Konstruktor gibt es einen Default-Konstruktor (leer)
  • Konstruktor-Ueberladung ermoeglicht mehrere Erstellungswege
  • this() ruft einen anderen Konstruktor derselben Klasse auf
  • Konstruktoren sind ideal fuer Validierung von Eingabewerten
  • Records haben kompakte Konstruktoren fuer Validierung und Normalisierung

Pro-Tipp: In IntelliJ kannst du Konstruktoren automatisch generieren lassen: Alt + Insert (oder Cmd + N auf macOS) > “Constructor”. Waehle die gewuenschten Felder aus, und IntelliJ erstellt den Konstruktor fuer dich — inklusive this-Zuweisungen. Das spart Zeit und vermeidet Tippfehler!

Zurück zum Java Kurs