1. Einleitung
In Java sind Zugriffsmodifikatoren wie ein Schließsystem in einem Haus. Sie bestimmen, wer von wo „in Ihr Zimmer eintreten“ darf (oder zu einem Feld/einer Methode Ihrer Klasse). Wenn alle Türen offen sind – kann jeder kommen und etwas ändern. Wenn alles verschlossen ist – kann niemand etwas kaputt machen, aber Sie könnten sich selbst irgendwann eingeschlossen wiederfinden.
Zur Erinnerung: In Java gibt es vier grundlegende Zugriffsebenen:
| Modifikator | Innerhalb der Klasse | Im Paket | In Unterklassen | In anderen Paketen |
|---|---|---|---|---|
|
✔ | |||
| (package) | ✔ | ✔ | ||
|
✔ | ✔ | ✔ | (über Vererbung) |
|
✔ | ✔ | ✔ | ✔ |
(package) – bedeutet, dass kein Modifikator explizit angegeben ist. Ein solches Klassenmitglied ist nur innerhalb eines einzelnen Pakets sichtbar.
2. Typische Fehler mit Zugriffsmodifikatoren
Fehler 1: Felder und Methoden mit dem Standardmodifikator (package-private)
Der häufigste Fehler von Einsteigern ist, den Zugriffsmodifikator zu vergessen. Dadurch wird ein Feld oder eine Methode im gesamten Paket zugänglich, obwohl Sie das nicht geplant haben. Das kann dazu führen, dass eine andere Klasse (im selben Paket, aber nicht mit Ihrer verwandt) den internen Zustand Ihres Objekts verändert.
// Fehler: Das Feld name ist nicht geschützt!
class User {
String name; // package-private!
}
Als Ergebnis kann jede Klasse aus diesem Paket Folgendes schreiben:
User user = new User();
user.name = "John"; // ohne Einschränkungen!
Fehler 2: Verletzung der Kapselung – offene Felder (public)
Der zweithäufigste Fehler ist, Felder einer Klasse als public zu deklarieren. Das ist bequem, wenn man gerade lernt oder ein kurzes Beispiel schreibt, in realen Projekten ist es jedoch fast immer schlecht. Sie verlieren die Kontrolle darüber, wer und wie Ihre Daten ändert.
public class Account {
public double balance; // GEFÄHRLICH!
}
Nun kann beliebiger Code Folgendes tun:
Account acc = new Account();
acc.balance = -1000000; // Und wer ist jetzt schuld?
Fehler 3: Fehlende Getter und Setter
Manchmal macht ein Entwickler Felder private, vergisst aber, Methoden zu deren Verwaltung hinzuzufügen. Infolgedessen kann der Wert nicht einmal dort gelesen oder geändert werden, wo es sinnvoll wäre.
public class Product {
private String name;
// Weder getName() noch setName() vorhanden
}
Fehler 4: Versuch, auf private-Member aus einer anderen Klasse zuzugreifen
Wenn Sie ein Feld oder eine Methode als private deklarieren, kann nicht aus einer anderen Klasse darauf zugegriffen werden, selbst wenn sie sich im selben Paket befindet. Einsteiger wundern sich oft, warum das Feld „nicht sichtbar“ ist.
public class User {
private String password;
}
public class UserService {
public void resetPassword(User user) {
// user.password = "123"; // Kompilierfehler!
}
}
Fehler 5: Fehler mit protected
Viele meinen, dass protected „überall sichtbar ist, wo es Vererbung gibt“. In Java ist der Zugriff auf protected-Member außerhalb des Pakets jedoch nur über Vererbung und nur für die Unterklasse möglich. Diese Feinheit wird leicht übersehen.
package animals;
public class Animal {
protected void sleep() {}
}
package zoo;
import animals.Animal;
public class Dog extends Animal {
public void test() {
sleep(); // OK – Unterklasse
}
}
public class NotADog {
public void test() {
Animal a = new Animal();
// a.sleep(); // Fehler: keine Unterklasse!
}
}
3. Wie es richtig geht: Best Practices
Regel 1: Machen Sie Felder standardmäßig private
Das ist das Hauptprinzip der Kapselung. Felder sollten vor allen außer der Klasse selbst verborgen sein. Wenn Zugriff nötig ist – verwenden Sie Getter/Setter.
public class Book {
private String title;
private int pages;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
Regel 2: Öffnen Sie nur notwendige Methoden
Wenn eine Methode von außen zugänglich sein soll – machen Sie sie public. Wenn sie nur innerhalb des Pakets benötigt wird – lassen Sie sie package-private. Wenn die Methode nur für Unterklassen gedacht ist – verwenden Sie protected.
Regel 3: Minimieren Sie den Sichtbarkeitsbereich
Je kleiner der Sichtbarkeitsbereich, desto geringer die Wahrscheinlichkeit für zufällige Fehler und „unerwartete Gäste“. Machen Sie Methoden und Felder nicht public, wenn es nicht nötig ist.
Regel 4: Verwenden Sie Getter und Setter zur Zugriffskontrolle
So können Sie zusätzliche Logik beim Lesen/Schreiben eines Feldes hinzufügen, zum Beispiel Validierung.
public class Account {
private double balance;
public void setBalance(double balance) {
if (balance < 0) {
throw new IllegalArgumentException("Der Kontostand darf nicht negativ sein!");
}
this.balance = balance;
}
public double getBalance() {
return balance;
}
}
Regel 5: Geben Sie die interne Implementierung nicht preis
Wenn Sie ein Array oder eine Liste als Feld haben, geben Sie sie nicht direkt über den Getter zurück – geben Sie eine Kopie zurück oder stellen Sie nur die benötigten Methoden bereit.
public class Team {
private List<String> members = new ArrayList<>();
// Korrekt:
public List<String> getMembers() {
return new ArrayList<>(members); // geben eine Kopie zurück
}
}
4. Praxisbeispiele
Angenommen, wir haben die Klasse LibraryUser, die einen Bibliotheksnutzer beschreibt.
Beispiel einer falschen Implementierung
public class LibraryUser {
public String name;
public int borrowedBooks;
}
In dieser Form kann beliebiger Code mit dem Objekt alles Mögliche tun:
LibraryUser user = new LibraryUser();
user.name = null;
user.borrowedBooks = -10; // Logik? Welche Logik?
Beispiel einer korrekten Implementierung mit Kapselung
public class LibraryUser {
private String name;
private int borrowedBooks;
public LibraryUser(String name) {
this.name = name;
this.borrowedBooks = 0;
}
public String getName() {
return name;
}
public int getBorrowedBooks() {
return borrowedBooks;
}
public void borrowBook() {
borrowedBooks++;
}
public void returnBook() {
if (borrowedBooks > 0) {
borrowedBooks--;
}
}
}
Externer Code kann nun nicht mehr direkt die Anzahl der ausgeliehenen Bücher oder den Namen des Nutzers ändern. Alles wird ausschließlich über die Methoden der Klasse gesteuert.
5. Besonderheiten und Feinheiten der Implementierung
Manchmal scheint es einfacher, ein Feld public zu machen, als viele Getter und Setter zu schreiben. Aber das ist eine Falle! Ein offenes Feld ist wie eine offene Wohnungstür: ja, bequem, aber nicht besonders sicher.
Eine weitere Feinheit: Nicht immer müssen Sie für alle Felder Getter und Setter bereitstellen. Wenn der Wert eines Feldes nach der Objekterstellung nicht mehr geändert werden soll, machen Sie nur einen Getter und das Feld final:
public class Passport {
private final String number;
public Passport(String number) {
this.number = number;
}
public String getNumber() {
return number;
}
}
Denken Sie außerdem daran: Wenn eine Klasse als public deklariert ist, muss der Dateiname mit dem Klassennamen übereinstimmen! Das hat nicht direkt mit Zugriffsmodifikatoren zu tun, ist aber ein sehr häufiger Anfängerfehler.
6. Häufige Fehler beim Arbeiten mit Zugriffsmodifikatoren
Fehler Nr. 1: Sie haben den Zugriffsmodifikator für ein Feld oder eine Methode nicht angegeben. Infolgedessen wird das Feld oder die Methode im gesamten Paket zugänglich, selbst wenn Sie das nicht wollten. Geben Sie den Modifikator immer explizit an, auch wenn die IDE nicht meckert.
Fehler Nr. 2: Alle Felder sind als public deklariert. Das zerstört die Kapselung, macht Ihren Code angreifbar und unvorhersehbar. Die „der Einfachheit halber“-Gewohnheit aus Beispielen sollte nicht in Produktionscode landen.
Fehler Nr. 3: Versuch, auf ein private-Feld aus einer anderen Klasse zuzugreifen. Java erlaubt das nicht – der Compiler schützt Sie. Wenn Sie jedoch versuchen, das über Reflection „zu umgehen“, überlegen Sie, warum das überhaupt nötig ist.
Fehler Nr. 4: Erwartung, dass protected-Member überall verfügbar sind, wo es Vererbung gibt. Tatsächlich kann außerhalb des Pakets nur aus einer Unterklasse darauf zugegriffen werden und nur über this oder über ein Objekt der Unterklasse.
Fehler Nr. 5: Zurückgeben der internen Collection über einen Getter. Wenn Sie eine Referenz auf das interne Array oder die Liste zurückgeben, kann externer Code sie ändern und damit die Invarianten der Klasse verletzen.
Fehler Nr. 6: Keine Prüfung beim Setzen eines Werts über einen Setter. Ohne Validierung können Sie einen inkorrekten Objektzustand erhalten (zum Beispiel einen negativen Kontostand).
Fehler Nr. 7: Zu großer Sichtbarkeitsbereich von Methoden. Manchmal werden Methoden public gemacht, obwohl sie nur innerhalb des Pakets oder der Klasse benötigt werden. Das öffnet unnötige APIs und erschwert die Wartung.
GO TO FULL VERSION