Zum Inhalt springen
Java Anfänger 25 min

Kapselung (Getter/Setter)

Kapselung in Java: private Felder, Getter und Setter, Validierung und warum Datenkapselung so wichtig ist. Mit Best Practices und Records.

Aktualisiert:

Kapselung (Encapsulation) in Java

Stell dir vor, jeder koennte direkt auf dein Bankkonto zugreifen und den Kontostand aendern — ohne Pruefung, ohne Limit. Klingt gefaehrlich, oder? Kapselung loest genau dieses Problem: Sie schuetzt die internen Daten eines Objekts und kontrolliert den Zugriff.

Was ist Kapselung?

Kapselung bedeutet:

  1. Felder private machen (kein direkter Zugriff von aussen)
  2. Zugriff ueber Getter und Setter kontrollieren
  3. Validierung in den Settern durchfuehren

Ohne Kapselung — gefaehrlich

public class Bankkonto {
    String inhaber;
    double kontostand; // Jeder kann das aendern!
}

var konto = new Bankkonto();
konto.kontostand = -999999; // Negativer Kontostand? Kein Problem!
konto.inhaber = "";          // Leerer Name? Geht auch!

Mit Kapselung — sicher

public class Bankkonto {
    private String inhaber;
    private double kontostand;

    public Bankkonto(String inhaber, double startguthaben) {
        setInhaber(inhaber);
        if (startguthaben < 0) {
            throw new IllegalArgumentException("Startguthaben darf nicht negativ sein!");
        }
        this.kontostand = startguthaben;
    }

    // Getter
    public String getInhaber() {
        return inhaber;
    }

    public double getKontostand() {
        return kontostand;
    }

    // Setter mit Validierung
    public void setInhaber(String inhaber) {
        if (inhaber == null || inhaber.isBlank()) {
            throw new IllegalArgumentException("Inhaber darf nicht leer sein!");
        }
        this.inhaber = inhaber;
    }

    // Kein Setter fuer Kontostand! Nur ueber Methoden aenderbar.
    public void einzahlen(double betrag) {
        if (betrag <= 0) {
            throw new IllegalArgumentException("Betrag muss positiv sein!");
        }
        kontostand += betrag;
    }

    public void abheben(double betrag) {
        if (betrag <= 0) {
            throw new IllegalArgumentException("Betrag muss positiv sein!");
        }
        if (betrag > kontostand) {
            throw new IllegalArgumentException("Nicht genug Guthaben!");
        }
        kontostand -= betrag;
    }
}
var konto = new Bankkonto("Max", 1000);
konto.einzahlen(500);       // OK
konto.abheben(200);         // OK
// konto.kontostand = -999; // FEHLER! kontostand ist private
// konto.abheben(50000);    // IllegalArgumentException!

Getter und Setter

Konventionen

TypGetterSetter
AllgemeingetXyz()setXyz(Typ wert)
booleanisXyz()setXyz(boolean wert)
public class Person {
    private String name;
    private int alter;
    private boolean aktiv;

    // Getter
    public String getName() { return name; }
    public int getAlter() { return alter; }
    public boolean isAktiv() { return aktiv; } // boolean: is statt get

    // Setter
    public void setName(String name) {
        if (name != null && !name.isBlank()) {
            this.name = name;
        }
    }

    public void setAlter(int alter) {
        if (alter >= 0 && alter <= 150) {
            this.alter = alter;
        }
    }

    public void setAktiv(boolean aktiv) {
        this.aktiv = aktiv;
    }
}

Nicht jedes Feld braucht einen Setter

Manchmal soll ein Feld nach der Erstellung nicht mehr aenderbar sein:

public class Mitarbeiter {
    private final String mitarbeiterId; // Unveraenderlich!
    private String name;
    private double gehalt;

    public Mitarbeiter(String id, String name, double gehalt) {
        this.mitarbeiterId = id;
        this.name = name;
        this.gehalt = gehalt;
    }

    // Nur Getter fuer ID -- kein Setter!
    public String getMitarbeiterId() { return mitarbeiterId; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public double getGehalt() { return gehalt; }

    // Kontrollierte Gehaltsaenderung
    public void gehaltsErhoehung(double prozent) {
        if (prozent > 0 && prozent <= 50) {
            this.gehalt *= (1 + prozent / 100);
        }
    }
}

Zugriffsmodifizierer im Ueberblick

ModifiziererKlassePaketUnterklasseUeberall
privateJaNeinNeinNein
(kein/package)JaJaNeinNein
protectedJaJaJaNein
publicJaJaJaJa

Faustregel:

  • Felder: immer private
  • Getter: public (wenn Zugriff noetig)
  • Setter: public mit Validierung (wenn Aenderung erlaubt)
  • Hilfsmethoden: private (interne Logik)
  • API-Methoden: public (oeffentliche Schnittstelle)

Records und Kapselung

Records bieten Kapselung ohne Boilerplate. Felder sind automatisch private final:

record Student(String name, int matrikelnummer, double note) {
    // Validierung im kompakten Konstruktor
    Student {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("Name darf nicht leer sein!");
        }
        if (note < 1.0 || note > 5.0) {
            throw new IllegalArgumentException("Note muss zwischen 1.0 und 5.0 liegen!");
        }
    }

    // Getter werden automatisch generiert: name(), matrikelnummer(), note()
    // KEINE Setter -- Records sind unveraenderlich (immutable)!

    boolean hatBestanden() {
        return note <= 4.0;
    }
}

var student = new Student("Max", 12345, 2.3);
System.out.println(student.name());          // Max
System.out.println(student.hatBestanden());  // true
// student.setName("Anna"); // FEHLER! Keine Setter bei Records

Vergleich mit Python

# Python: Konvention mit Unterstrich
class Bankkonto:
    def __init__(self, inhaber, kontostand):
        self._inhaber = inhaber       # "private" (Konvention)
        self.__kontostand = kontostand # Name Mangling

    @property
    def kontostand(self):
        return self.__kontostand

    @kontostand.setter
    def kontostand(self, wert):
        if wert >= 0:
            self.__kontostand = wert
// Java: Echte Zugriffskontrolle
public class Bankkonto {
    private String inhaber;      // Wirklich private!
    private double kontostand;   // Wirklich private!

    public double getKontostand() {
        return kontostand;
    }
    // Kein Setter -- nur ueber einzahlen/abheben
}

In Python ist _private nur eine Konvention — man kann trotzdem darauf zugreifen. In Java ist private erzwungen durch den Compiler.

Immutable Objects (Unveraenderliche Objekte)

Das sicherste Muster: Objekte, die nach der Erstellung nicht mehr geaendert werden koennen:

public final class Adresse {
    private final String strasse;
    private final String plz;
    private final String stadt;

    public Adresse(String strasse, String plz, String stadt) {
        this.strasse = strasse;
        this.plz = plz;
        this.stadt = stadt;
    }

    // Nur Getter, keine Setter
    public String getStrasse() { return strasse; }
    public String getPlz() { return plz; }
    public String getStadt() { return stadt; }

    // "Aenderung" erzeugt ein neues Objekt
    public Adresse mitStadt(String neueStadt) {
        return new Adresse(this.strasse, this.plz, neueStadt);
    }
}

Oder einfacher als Record:

record Adresse(String strasse, String plz, String stadt) {
    Adresse mitStadt(String neueStadt) {
        return new Adresse(strasse, plz, neueStadt);
    }
}

Best Practices

1. Felder immer private

// SCHLECHT:
public class Student {
    public String name;
    public int alter;
}

// GUT:
public class Student {
    private String name;
    private int alter;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

2. Validierung im Setter

public void setEmail(String email) {
    if (email == null || !email.contains("@")) {
        throw new IllegalArgumentException("Ungueltige E-Mail!");
    }
    this.email = email.toLowerCase().strip();
}

3. Unveraenderliche Felder mit final

private final String id;     // Wird nie geaendert
private final LocalDate erstelltAm;

4. Defensive Kopien

public class Kurs {
    private final List<String> teilnehmer;

    public Kurs(List<String> teilnehmer) {
        // Defensive Kopie -- Aenderungen an der originalen Liste
        // beeinflussen das Kurs-Objekt nicht
        this.teilnehmer = new ArrayList<>(teilnehmer);
    }

    public List<String> getTeilnehmer() {
        // Unveraenderliche Kopie zurueckgeben
        return List.copyOf(teilnehmer);
    }
}

Uebungen

Uebung 1: Produkt-Klasse

Erstelle eine Klasse Produkt mit gekapselten Feldern (name, preis, bestand). Validiere: Name nicht leer, Preis > 0, Bestand >= 0.

Uebung 2: Temperatur

Erstelle eine Klasse Temperatur mit einem privaten Feld celsius. Biete Getter fuer Celsius, Fahrenheit und Kelvin. Validiere im Setter.

Uebung 3: Passwort-Manager

Erstelle eine Klasse mit gekapseltem Passwort. Das Passwort soll nur ueber setPasswort() geaendert werden koennen (mit Laengen- und Komplexitaetspruefung). getPasswort() soll das Passwort nicht im Klartext zurueckgeben.

Uebung 4: Warenkorb

Erstelle eine Klasse Warenkorb mit einer privaten Liste von Produkten. Biete Methoden hinzufuegen(), entfernen(), gesamtpreis() und getProdukte() (als unveraenderliche Kopie).

Was kommt als Naechstes?

In der naechsten Lektion lernst du Enums — eine spezielle Klasse fuer fest definierte Wertegruppen wie Wochentage, Farben oder Status-Codes.

Zusammenfassung

  • Kapselung schuetzt Daten durch private Felder und kontrollierte Zugriffsmethoden
  • Getter lesen Werte, Setter schreiben Werte (mit Validierung)
  • Nicht jedes Feld braucht einen Setter — manche sind unveraenderlich (final)
  • Records bieten automatische Kapselung ohne Boilerplate
  • Immutable Objects sind die sicherste Form der Kapselung
  • Defensive Kopien schuetzen interne Collections vor Manipulation
  • Faustregel: Felder immer private, Zugriff nur ueber kontrollierte Methoden

Pro-Tipp: In IntelliJ generierst du Getter und Setter blitzschnell: Alt + Insert > “Getter and Setter” > Felder auswaehlen > fertig! Noch besser: Ueberlege bei jedem Feld, ob es wirklich einen Setter braucht. Oft ist ein unveraenderliches Objekt (oder ein Record) die bessere Wahl. Je weniger Setter, desto weniger Bugs!

Zurück zum Java Kurs