Kapselung
Kurz gesagt: Kapselung bedeutet, dass eine Klasse ihre Daten mit private versteckt und sie nur über ihre eigenen Methoden zeigt, über Getter und Setter, die die Werte prüfen.
Problem
Minus 500 €
In der letzten Lektion haben wir die Prüfung beim Abheben in eine Methode gesteckt. Das Feld ist aber offen, und jeder kann direkt hineinschreiben:
Account account = new Account("Maria", 100);account.withdraw(500);account.balance = -500;System.out.println(account.balance);-500.0Die Methode hat das Abheben von 500 € richtig abgelehnt. Die nächste Zeile hat den Kontostand einfach von Hand gesetzt. Die Prüfung ist da, aber man kann sie umgehen. Wir brauchen eine Möglichkeit zu sagen: „Nur das Konto fasst den Kontostand an.“
Die Idee
private: nur die Klasse fasst es an
Schreib private vor das Feld statt public:
class Account { private String owner; private double balance; // ...}Jetzt kommt nur noch der Code in der Klasse Account an das Feld. Von außen lässt sich die Zeile
account.balance = -500;nicht mehr kompilieren:
error: balance has private access in Accountprivate und public heißen Zugriffsmodifizierer. Sie sagen, wer ein Feld, eine Methode oder einen Konstruktor sehen darf. Die Klasse versteckt ihre Daten und zeigt sie nur über ihre eigenen Methoden. Das nennt man Kapselung.
Genauer, für Neugierige
- Ohne Modifizierer ist ein Feld oder eine Methode im selben Paket sichtbar (einer Gruppe von Klassen in einem Ordner). Solange alles in einer Datei steht, ist das fast wie
public. Schreib den Modifizierer deshalb ausdrücklich hin. - Es gibt auch
protected: sichtbar in der Klasse und in ihren Unterklassen. Du triffst es im Modul „Vererbung und Polymorphie“. public class Mainist eine öffentliche Klasse. Eine Datei kann nur eine davon haben, und sie heißt wie die Datei. Deshalb haben unsere Klassen darüber keinpublic.
Die Idee
Lesen, ohne anzufassen
Von außen können wir den Kontostand jetzt nicht einmal lesen. Deshalb gibt die Klasse eine Methode, die ihn nur zurückgibt:
class Account { private String owner; private double balance;
public Account(String owner, double balance) { this.owner = owner; this.balance = balance; }
public String getOwner() { return owner; }
public double getBalance() { return balance; }
public void deposit(double amount) { if (amount > 0) { balance += amount; } }
public boolean withdraw(double amount) { if (amount > 0 && amount <= balance) { balance -= amount; return true; } return false; }}
public class Main { public static void main(String[] args) { Account account = new Account("Maria", 100); account.withdraw(30); account.deposit(50); System.out.println(account.getOwner() + ": " + account.getBalance() + " €"); }}Maria: 120.0 €getBalance() ist ein Getter: eine Methode, die den Wert eines Feldes zurückgibt. Ihr Name ist get plus der Name des Feldes mit großem Anfangsbuchstaben. Der Kontostand ändert sich nur über deposit und withdraw, und die prüfen.
withdraw gibt jetzt boolean zurück: ob das Abheben geklappt hat. So weiß jeder, der abhebt, ob es funktioniert hat.
Account account = new Account("Maria", 100);account.withdraw(500);account.deposit(50);System.out.println(account.getBalance());Schritt für Schritt, in Worten
- balance ist private: Das Glas bedeutet, dass von außen kein Weg zum Feld führt.
- Das Abheben geht durch die Prüfung in der Methode: 500 ist mehr als 100. Abgelehnt, und der Kontostand bleibt, wie er ist.
- Auch die Einzahlung wird geprüft: 50 ist ein positiver Betrag. Die Methode addiert ihn.
- getBalance() gibt eine Kopie des Kontostands heraus. Das Feld selbst bleibt versteckt.
Probier es aus
Abgelehnt
Was gibt das Programm aus?
Account account = new Account("Maria", 100);System.out.println(account.withdraw(150));account.deposit(-20);System.out.println(account.getBalance());150 € sind mehr als der Kontostand, also gibt withdraw false zurück und lässt den Kontostand in Ruhe. Die Einzahlung von -20 € wird ebenfalls abgelehnt, weil der Betrag positiv sein muss. Der Kontostand bleibt 100.
Die Idee
Setter: eine Änderung mit Prüfung
Manchmal muss Code von außen ein Feld ändern, aber nicht auf jeden beliebigen Wert. Das Alter eines Tieres kann sich ändern, aber es kann nicht negativ sein:
class Animal { private int age;
public int getAge() { return age; }
public void setAge(int age) { if (age >= 0) { this.age = age; } }}
Animal cat = new Animal();cat.setAge(4);cat.setAge(-3);System.out.println(cat.getAge());4setAge ist ein Setter: eine Methode, die einen Wert in einem Feld speichert, nachdem sie ihn geprüft hat. Bei -3 schlägt die Prüfung fehl, und das Alter bleibt 4.
Die Idee
Welches Feld bekommt einen Setter?
Nicht jedes Feld braucht einen Setter, und manche brauchen nicht einmal einen Getter. Überleg, was Code von außen mit dem Produkt tun können soll:
- Name und Preis werden beim Erzeugen des Produkts gesetzt und danach nur gelesen: nur ein Getter;
- die Stückzahl im Warenkorb ändert sich, ist aber mindestens 1: ein Getter und ein Setter mit Prüfung.
class Product { private String name; private double price; private int quantity;
public Product(String name, double price) { this.name = name; this.price = price; this.quantity = 1; }
public String getName() { return name; }
public double getPrice() { return price; }
public int getQuantity() { return quantity; }
public void setQuantity(int quantity) { if (quantity >= 1) { this.quantity = quantity; } }}
Product coffee = new Product("Kaffee", 7.50);coffee.setQuantity(3);coffee.setQuantity(0);System.out.println(coffee.getName() + " x" + coffee.getQuantity());Kaffee x3Die Regel lautet: Fang mit private für alles an und öffne nur, was du brauchst. Ein Setter, der ohne Prüfung alles speichert, bringt das Problem aus der ersten Karte zurück.
In Java schreibst du Getter und Setter von Hand. IntelliJ IDEA kann sie erzeugen: Alt+Insert → Getter and Setter.
Probier es aus
Was fehlt?
class Account {
double balance;
}private: Das Feld ist nur für den Code in Account sichtbar.Probier es aus
Finde den Fehler
Das Feld ist private, und trotzdem wird der Kontostand -500:
Account account = new Account();account.setBalance(100);account.withdraw(30);account.setBalance(-500);System.out.println(account.getBalance());-500.0Welche Zeile in der Klasse bricht den Schutz?
Wähle die Zeile mit dem Fehler.
setBalance speichert jeden Wert ohne Prüfung. Das Feld ist versteckt, aber der Setter öffnet es wieder. Entferne ihn und setz den Startbetrag im Konstruktor. Danach ändert er sich nur über deposit und withdraw.
Übung
Versteckte Daten
Löse die Aufgaben in deinem Editor. Alle Felder sind private.
1. Person. Person mit Name und Alter, ein Konstruktor, die Getter getName() und getAge() und introduce().
Maria ist 34 Jahre alt.Hallo, ich bin Maria und bin 34 Jahre alt.2. Ein Konto ohne Minus. Account mit Inhaber und Kontostand. Eine Einzahlung nimmt nur einen positiven Betrag an. Eine Abhebung gibt zurück, ob sie geklappt hat, und lässt den Kontostand nie unter 0 fallen. Code von außen kann den Kontostand lesen, aber nicht schreiben.
Account account = new Account("Peter", 50);account.deposit(25);account.deposit(-10);System.out.println(account.withdraw(100));System.out.println(account.withdraw(40));System.out.println("Kontostand: " + account.getBalance() + " €");falsetrueKontostand: 35.0 €3. Tier. Animal mit Art und Alter. Die Art wird im Konstruktor gesetzt und danach nur gelesen. Das Alter hat einen Setter, der eine negative Zahl ablehnt und eine Meldung ausgibt. Auch der Konstruktor geht durch dieselbe Prüfung.
Animal cat = new Animal("Katze", 3);cat.setAge(4);cat.setAge(-1);System.out.println(cat.getSpecies() + ", " + cat.getAge() + " Jahre");Ungültiges Alter: -1Katze, 4 JahreWeitere Aufgaben
- Note. Eine Klasse
Grademit dem Namen eines Fachs und einer Note von 2 bis 6. Der Setter lehnt alles außerhalb dieser Grenzen ab. Probier es mit 5, 7 und 1. - Produkt mit Rabatt. Gib dem
Productaus der Lektion eine Methode, die einen Rabatt in Prozent anwendet und nur 1 bis 90% annimmt. Der Preis hat keinen Setter.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Parameter und return · Klassen und Objekte · Konstruktoren und this · Instanzmethoden
- Geht weiter in: static: was alle Objekte teilen · Vererbung · Polymorphie · Abstrakte Klassen
- Aufgaben zum Thema: Klassen und Objekte