Klassen implementieren: Konstruktor, Getter, Setter und Objekterzeugung
https://bildung.social/@oerinformatik/
https://oer-informatik.de/klassen-implementieren
Objektorientierte Programmierung, Klasse, Konstruktor, Getter und Setter, Kapselung, Pseudocode, UML-Klassendiagramm, Vererbung, Klassenattribut, Referenzsemantik
tl/dr; (ca. 27 min Lesezeit): Ein Klassendiagramm ist erst der Bauplan. Dieser Artikel zeigt, wie Du daraus Code machst: Konstruktoren, die Objekte in einen gültigen Anfangszustand bringen, Getter und Setter mit Prüfungen, Objekterzeugung mit
new, Konstruktoren in Unterklassen mitsuper(…), fortlaufende Nummern über Klassenattribute und was bei der Übergabe von Objekten und Arrays an Methoden wirklich passiert. Alles in Pseudocode, wie er in Aufgaben üblich ist, und zum Vergleich in Java und Python [AI-Assisted]. (Zuletzt geändert am 28.10.2026)
Dieser Artikel baut auf der Kann-Liste der Artikel Grundlagen des UML-Klassendiagramms und Sichtbarkeitsmodifikatoren auf. Übungsaufgaben gibt es im Artikel Übungsaufgaben zum Implementieren von Klassen.
Schon fit in diesem Thema? Nutze die Kann-Liste und die Leitfragen unter dem Artikel als Checkliste, ob Du die relevanten Kompetenzen bereits hast.
Als Grundlagen zum Implementieren von Klassen kann ich nach Durchlesen des Artikels…
… aus einem UML-Klassendiagramm das Gerüst einer Klasse in Pseudocode ableiten und umgekehrt eine beschriebene Klasse mit sinnvollen Datentypen als Klassendiagramm darstellen.
… einen Konstruktor implementieren, der alle Attribute aus den Parametern initialisiert, und dabei erklären, warum er keinen Rückgabetyp hat und wozu
thisdient.… Getter und Setter implementieren, einen Setter mit Gültigkeitsprüfung schreiben und begründen, wann ein Attribut nur einen Getter bekommt.
… Objekte mit
newerzeugen, sie Variablen zuweisen, an andere Objekte übergeben und deren Methoden aufrufen, ohne auf private Attribute zuzugreifen.… erläutern, warum eine Methode ohne Rückgabewert trotzdem Daten an den Aufrufer liefern kann, wenn sie ein übergebenes Objekt oder Array verändert.
… typische Fehler in Konstruktoren, Gettern und Settern erkennen und korrigieren.
Darüber hinaus kann ich als Expertenwissen…
… den Konstruktor einer Unterklasse mit
super(…)implementieren und geschützte (protected) Attribute der Oberklasse nutzen.… einen Konstruktor schreiben, der das neue Objekt mit
thisbei einem anderen Objekt anmeldet.… Konstruktoren überladen, mit
this(…)verketten und fortlaufende Nummern über ein Klassenattribut vergeben.… Wert- und Referenzsemantik unterscheiden und vorhersagen, welche Änderungen nach einem Methodenaufruf beim Aufrufer sichtbar sind.
Im Klassendiagramm steht, was eine Klasse hat und kann: Attribute, Methoden, Sichtbarkeiten. Wie die Methoden arbeiten, steht dort nicht. In der Praxis und in Aufgaben folgt deshalb auf das Modell fast immer der nächste Schritt: Du sollst den Konstruktor, einen Getter oder Setter oder ein paar Zeilen schreiben, die Objekte erzeugen und miteinander verbinden. Diese Aufgaben sind kurz und folgen wenigen festen Regeln; wenn Du sie sicher beherrschst, schreibst Du solchen Code schnell und fehlerfrei.
Als durchgehendes Beispiel dient die Software einer Schulbibliothek mit Büchern, Fachbüchern, Leser*innen und Ausleihen.
Vom Klassendiagramm zum Klassengerüst
Jedes Element des Klassendiagramms hat eine feste Entsprechung im Code. Wenn Du diese Übersetzung beherrschst, ist das Gerüst einer Klasse in einer Minute geschrieben:
| Im Klassendiagramm | Im Pseudocode | Bedeutung |
|---|---|---|
-titel: String |
- String titel |
privates Attribut, nur in der eigenen Klasse sichtbar |
-verfuegbar: Boolean = true |
- Boolean verfuegbar = true |
Attribut mit Startwert |
-{static} naechsteNummer: Integer = 1001 (unterstrichen) |
- static Integer naechsteNummer = 1001 |
Klassenattribut: gibt es nur einmal für alle Objekte |
#seriennummer: String |
# String seriennummer |
geschützt: auch in Unterklassen sichtbar |
+Buch(isbn: String, …) |
+ Buch(String isbn, …) { … } |
Konstruktor: Name der Klasse, kein Rückgabetyp |
+getTitel(): String |
+ getTitel(): String { … } |
Methode mit Rückgabe |
+setTitel(titel: String) |
+ setTitel(String titel) { … } |
Methode ohne Rückgabe (in Java void) |
Generalisierungspfeil von Fachbuch zu Buch |
class Fachbuch extends Buch |
Fachbuch erbt von Buch |
Assoziation Ausleihe "0..*" -- "1" Buch |
- Buch buch in Ausleihe |
Eine Beziehung wird im Code zu einem Attribut, das die Referenz speichert. |
Die letzte Zeile ist wichtig: Im Klassendiagramm steht ein Attribut für eine Beziehung meist nicht zusätzlich in der Klasse, im Code brauchst Du es aber. Die Klasse Ausleihe bekommt also die Attribute buch und leser, obwohl im Diagramm nur rueckgabeBis eingetragen ist.
Welche Datentypen sinnvoll sind, ergibt sich aus der Bedeutung: Bezeichnungen und Nummern mit führenden Nullen oder Bindestrichen (ISBN, Kennzeichen, Postleitzahl) sind String, Mengen und Zähler Integer, Geldbeträge und Messwerte Real (in Java double; für Geld in echter Software besser BigDecimal), Ja/Nein-Angaben Boolean, Datumswerte ein Datumstyp (Datum, in Java LocalDate), Bilder z. B. Image. Die UML-Spezifikation (UML 2.5.1, Abschnitt 21) kennt als Primitivtypen nur Integer, Real, Boolean, String und UnlimitedNatural; weitere Typen darfst Du verwenden, wenn sie vorgegeben oder eindeutig sind.
Pseudocode für Klassen
Pseudocode ist nicht genormt. Aufgaben verwenden meist eine Java-nahe Schreibweise, akzeptiert wird aber jede eindeutige Form, auch deutsche Schlüsselwörter („öffentlich“, „Rückgabe“, „Ende“) oder echter Code. In diesem Artikel gilt dieselbe Schreibweise wie im Artikel Objektorientierte Algorithmen:
| Was | Schreibweise | Varianten, die ebenfalls richtig sind |
|---|---|---|
| Methodenkopf | + setTitel(String titel) |
public void setTitel(String titel), + setTitel(titel: String), öffentlich setTitel(titel: String) |
| Methode mit Rückgabe | + getTitel(): String |
public String getTitel(), öffentlich getTitel(): String |
| Zuweisung | this.titel = titel |
this.titel := titel, this.titel ← titel |
| Rückgabe | return titel |
Rückgabe titel, return this.titel |
| Objekt erzeugen | Buch b = new Buch("978-3-16-148410-0", "Netzwerke für Einsteiger", 256) |
b := new Buch(…), b = neues Buch(…) |
| Methode aufrufen | b.setVerfuegbar(false) |
|
| eigenes Objekt | this |
self (Python) |
| Konstruktor der Oberklasse | super(isbn, titel, seiten) |
base(…) (C#), „Basisklassenkonstruktor(…)“ |
| kein Objekt | null |
None (Python), nichts |
Wichtig ist nicht die Schreibweise, sondern dass die Struktur erkennbar ist: ein vollständiger Methodenkopf mit Sichtbarkeit, Name, Parametern (mit Typ) und – falls vorhanden – Rückgabetyp, darunter die Anweisungen. Schon der richtige Methodenkopf zeigt, dass Du die Schnittstelle der Methode verstanden hast: welche Daten hineingehen und was herauskommt.
Der Konstruktor: Objekte in einen gültigen Anfangszustand bringen
Der Konstruktor (constructor) ist eine besondere Methode, die genau einmal läuft: wenn mit new ein Objekt erzeugt wird. Seine Aufgabe ist es, alle Attribute so zu belegen, dass das Objekt von Anfang an sinnvoll benutzbar ist.
+ Buch(String isbn, String titel, Integer seiten) {
this.isbn = isbn
this.titel = titel
setSeiten(seiten)
}
Daran erkennst Du einen Konstruktor:
Er heißt wie die Klasse (in Java und C#; in Python heißt er immer
__init__).Er hat keinen Rückgabetyp, auch nicht
void. Er gibt nichts mitreturnzurück. Das neue Objekt liefertnew, nicht der Konstruktor. Schreibst Du in Javapublic void Buch(…), ist das kein Konstruktor mehr, sondern eine gewöhnliche Methode, die zufällig wie die Klasse heißt.Er ist in der Regel öffentlich (
+), damit andere Klassen Objekte erzeugen dürfen.Er bekommt die Werte, die das Objekt von Anfang an braucht, als Parameter. Attribute mit Startwert (
verfuegbar = true) muss er nicht setzen.
Wozu this?
Parameter heißen meist genauso wie die Attribute, die sie füllen. Innerhalb des Konstruktors verdeckt der Parameter dann das gleichnamige Attribut. Mit this sprichst Du ausdrücklich das Attribut des aktuellen Objekts an:
this.titel = titel // Attribut des Objekts = Wert des Parameters
titel = titel // Fehler: Der Parameter wird sich selbst zugewiesen, das Attribut bleibt leer.
Heißen Parameter und Attribut verschieden (setTitel(String neuerTitel)), ist this nicht nötig, schadet aber auch nicht. In Python kannst Du darauf nicht verzichten: Dort heißt es immer self.titel = titel.
Prüfungen nicht doppelt schreiben
Im Beispiel setzt der Konstruktor seiten nicht direkt, sondern ruft den Setter auf. So steht die Prüfung „Seitenzahl muss positiv sein“ nur an einer Stelle, und auch neu erzeugte Objekte können keinen ungültigen Wert bekommen. Das ist eine bewährte Regel: Gibt es einen Setter mit Prüfung, nutzt der Konstruktor ihn.
Der Standardkonstruktor
Schreibst Du gar keinen Konstruktor, ergänzen Java und C# automatisch einen parameterlosen Standardkonstruktor (default constructor), der alle Attribute auf ihren Startwert (bzw. 0, false, null) setzt. Sobald Du selbst einen Konstruktor mit Parametern schreibst, entfällt dieser automatische Konstruktor. new Buch() funktioniert dann nicht mehr, wenn Du nicht ausdrücklich auch einen parameterlosen Konstruktor anlegst.
Konstruktoren im Klassendiagramm
Im Klassendiagramm steht der Konstruktor wie eine Operation ohne Rückgabetyp: +Buch(isbn: String, titel: String, seiten: Integer). Die UML-Spezifikation kennzeichnet Konstruktoren eigentlich mit dem Stereotyp <<Create>>. Heißt der Konstruktor wie die Klasse, ist er aber auch ohne Stereotyp eindeutig, und so wird er in vielen Aufgaben und auf dieser Website notiert.
Getter und Setter
Die Attribute sind privat. Damit andere Objekte sie trotzdem lesen oder ändern können, bekommt die Klasse öffentliche Zugriffsmethoden: Getter (lesen, accessor) und Setter (schreiben, mutator).
+ getTitel(): String {
return titel
}
+ setTitel(String titel) {
this.titel = titel
}
Das Muster ist immer gleich. Wer Deinen Code liest, erkennt Getter und Setter an genau diesem Aufbau, und viele Werkzeuge und Bibliotheken (etwa nach der JavaBeans-Konvention) setzen ihn voraus:
| Getter | Setter | |
|---|---|---|
| Name | get + Attributname, bei Boolean oft is…/ist… (istVerfuegbar()) |
set + Attributname |
| Parameter | keine | genau einer, vom Typ des Attributs |
| Rückgabetyp | Typ des Attributs | keiner (void) |
| Rumpf | return titel |
this.titel = titel |
Setter mit Prüfung
Der eigentliche Gewinn eines Setters ist, dass er ungültige Werte abfangen kann. Wie er reagiert, legt die Anforderung fest. Drei Varianten kommen häufig vor:
+ setSeiten(Integer seiten) { // 1. ungültigen Wert ignorieren
if (seiten > 0) {
this.seiten = seiten
}
}
+ setRabatt(Integer prozent) { // 2. auf die nächste Grenze setzen
if (prozent < 0) {
this.rabatt = 0
} else if (prozent > 50) {
this.rabatt = 50
} else {
this.rabatt = prozent
}
}
+ setEmail(String email): Boolean { // 3. Erfolg melden
if (not email.contains("@")) {
return false
}
this.email = email
return true
}
In echter Software wirft ein Setter bei ungültigen Werten oft eine Ausnahme (throw new IllegalArgumentException("Seitenzahl muss positiv sein")). Das zwingt den Aufrufer, sich um den Fehler zu kümmern. Verlangt eine Aufgabe nichts Bestimmtes, ist jede nachvollziehbare Variante richtig.
Nicht jedes Attribut braucht einen Setter
Getter und Setter werden nicht automatisch für jedes Attribut angelegt, sondern nur, wenn sie gebraucht werden:
Ein Attribut, das sich nach dem Erzeugen nie ändern darf (die ISBN eines Buchs, die Leser-Nummer), bekommt nur einen Getter. Es wird im Konstruktor gesetzt und ist danach schreibgeschützt. In Java kennzeichnet
finalsolche Attribute.Ein Attribut, das nur intern gebraucht wird (ein Zähler, eine Liste), bekommt oft gar keine Zugriffsmethode, sondern fachliche Methoden wie
ausleiheHinzufuegen(a).Ein Wert, der sich aus anderen ergibt, braucht kein eigenes Attribut:
getAlter()berechnet das Alter aus dem Geburtsdatum, statt ein Attributalterzu speichern, das jedes Jahr veralten würde. Im Klassendiagramm heißt so etwas abgeleitetes Attribut (/alter: Integer).
Warum nicht einfach öffentliche Attribute? Kapselung
Wäre seiten öffentlich, könnte jede Klasse buch.seiten = -5 schreiben. Das Objekt hätte keine Chance, sich zu wehren. Das Prinzip der Kapselung (encapsulation, auch Geheimnisprinzip) dreht das um: Die Daten eines Objekts sind privat, Zugriff gibt es nur über Methoden. Das bringt drei Vorteile:
Gültigkeit: Der Setter prüft jeden neuen Wert. Ungültige Zustände können gar nicht entstehen.
Kontrolle: Die Klasse entscheidet, was gelesen und was geändert werden darf (nur Getter, kein Setter).
Änderbarkeit: Die interne Darstellung kann sich ändern, ohne dass andere Klassen angepasst werden müssen. Speichert das Buch die Seitenzahl später anders, bleibt
getSeiten()gleich.
Eine Klasse darf innerhalb ihrer eigenen Methoden direkt auf ihre privaten Attribute zugreifen, auch auf die eines anderen Objekts derselben Klasse. Von außen ist dagegen jeder direkte Zugriff ein Fehler: ausleihe.leser.name ist falsch, richtig ist ausleihe.getLeser().getName(). In Python ist „privat“ nur eine Konvention (Unterstriche); mehr dazu im Artikel zur Kapselung in Python.
Objekte erzeugen und verwenden
Ein Objekt entsteht mit new und dem Konstruktor der Klasse. Die Argumente stehen in der Reihenfolge der Parameter im Klassendiagramm:
Buch b = new Buch("978-3-16-148410-0", "Netzwerke für Einsteiger", 256)
Leser l = new Leser("Aylin Yıldız")
Ausleihe a = new Ausleihe(b, l, new Datum(2026, 11, 16))
new Buch(…)reserviert Speicher für ein neues Objekt, ruft den Konstruktor auf und liefert eine Referenz auf das Objekt.Buch bist eine Variable, die diese Referenz speichert. Ihr Typ ist eine Klasse (oder ein Interface).Ohne
newentsteht kein Objekt:Buch b = Buch(…)ist in Java ein Fehler (in Python dagegen genau richtig:b = Buch(…)).
Nach diesen drei Zeilen gibt es drei Objekte. Die Ausleihe ist über Links mit dem Buch und dem Leser verbunden, im Code also über die Referenzen in ihren Attributen buch und leser:
Danach rufst Du Methoden über die Referenz auf und übergibst Objekte an Methoden anderer Objekte. Sehr häufig ist das Muster „Objekt aus Parametern erzeugen und einem anderen Objekt übergeben“:
+ messwertEmpfangen(Integer sensorId, Real wert, Datum zeit) {
Messwert m = new Messwert(sensorId, wert, zeit)
messreihe.hinzufuegen(m)
}
Die Variable m wird nach dem Methodenende nicht mehr gebraucht. Das Objekt bleibt trotzdem erhalten, weil messreihe eine Referenz darauf gespeichert hat. Ein Objekt verschwindet erst, wenn keine Referenz mehr darauf zeigt; dann räumt in Java, C# und Python die automatische Speicherbereinigung (garbage collection) auf.
Variablen vom Typ einer Oberklasse oder eines Interfaces
Der Typ der Variable muss nicht genau die Klasse des Objekts sein. Eine Variable vom Typ der Oberklasse oder eines implementierten Interfaces kann jedes passende Objekt aufnehmen:
Buch b = new Fachbuch("978-3-446-47126-2", "Datenbanken", 412, "Informatik")
Über b kannst Du alle Methoden von Buch aufrufen; welche Implementierung läuft, entscheidet das Objekt (mehr im Artikel zur Polymorphie). Diese Technik steckt auch hinter Entwurfsmustern, bei denen ein Objekt ein anderes „umhüllt“: Ein neues Objekt bekommt ein vorhandenes als Konstruktorparameter und wird derselben Variablen zugewiesen.
Quelle q = new Sensorquelle()
q = new Glaettungsfilter(q) // der Filter umhüllt die ursprüngliche Quelle
null: kein Objekt
Eine Referenzvariable kann auch auf kein Objekt zeigen; sie hat dann den Wert null. Rufst Du darauf eine Methode auf, bricht das Programm ab (in Java mit einer NullPointerException). Wenn ein Parameter null sein darf (ein Schloss ohne Schließanlage, ein Buch ohne Fachgebiet), prüfst Du das vorher mit if (anlage != null).
Objekte, die sich selbst bekannt machen: this als Argument
this ist eine Referenz auf das aktuelle Objekt und kann wie jede andere Referenz übergeben werden. Damit kann sich ein Objekt schon im Konstruktor bei einem anderen Objekt anmelden:
+ Ausleihe(Buch buch, Leser leser, Datum rueckgabeBis) {
this.buch = buch
this.leser = leser
this.rueckgabeBis = rueckgabeBis
leser.ausleiheHinzufuegen(this)
buch.setVerfuegbar(false)
}
Das Sequenzdiagramm zeigt die Reihenfolge der Aufrufe:
Nach new Ausleihe(…) kennt der Leser die neue Ausleihe, ohne dass der Aufrufer daran denken muss. Genau so registrieren sich Beobachter beim beobachteten Objekt (model.addObserver(this), siehe Beobachter-Muster). Achte auf die Reihenfolge: Erst die eigenen Attribute setzen, dann this weitergeben, denn das andere Objekt könnte sofort Methoden des neuen Objekts aufrufen.
Mehrere Konstruktoren: Überladung und this(…)
Eine Klasse darf mehrere Konstruktoren haben, wenn sich ihre Parameterlisten unterscheiden (Überladung, overloading; mehr im Artikel zum UML-Klassendiagramm). Damit der gemeinsame Code nicht doppelt geschrieben wird, ruft ein Konstruktor einen anderen mit this(…) auf:
+ Ausleihe(Buch buch, Leser leser) {
this(buch, leser, Datum.heute().plusTage(28)) // Standard: vier Wochen
}
this(…) muss in Java bis Version 24 die erste Anweisung im Konstruktor sein. Seit Java 25 (JEP 513, „Flexible Constructor Bodies“) dürfen davor Anweisungen stehen, die nicht auf this zugreifen, etwa eine Prüfung der Parameter. Üblich und in Aufgaben erwartet bleibt this(…) am Anfang. Python kennt keine Überladung; dort löst man das mit Standardwerten für Parameter (def __init__(self, buch, leser, rueckgabe_bis=None)).
Klassenattribute: fortlaufende Nummern vergeben
Jeder Leser soll eine eindeutige Nummer ab 1001 bekommen. Ein gewöhnliches Attribut hilft nicht, denn jedes Objekt hätte sein eigenes. Ein Klassenattribut (static, im Klassendiagramm unterstrichen) gibt es dagegen nur einmal für die ganze Klasse; alle Objekte teilen es:
class Leser {
- static Integer naechsteNummer = 1001
- Integer leserNr
- String name
+ Leser(String name) {
this.name = name
this.leserNr = naechsteNummer
naechsteNummer = naechsteNummer + 1
}
}
Der erste Leser bekommt 1001, der zweite 1002 und so weiter. Klassenattribute sprichst Du in der eigenen Klasse direkt an, von außen über den Klassennamen (Leser.naechsteNummer, falls öffentlich). Auf die gleiche Weise funktionieren statische Methoden wie Datum.heute().
Konstruktoren in Unterklassen: super(…)
Konstruktoren werden nicht vererbt. Eine Unterklasse braucht einen eigenen Konstruktor, und der muss dafür sorgen, dass auch der geerbte Teil des Objekts initialisiert wird. Das erledigt der Aufruf des Konstruktors der Oberklasse mit super(…):
class Fachbuch extends Buch {
- String fachgebiet
+ Fachbuch(String isbn, String titel, Integer seiten, String fachgebiet) {
super(isbn, titel, seiten)
this.fachgebiet = fachgebiet
}
}
Beim Erzeugen eines Fachbuchs initialisiert so jeder Konstruktor seinen Teil des Objekts:
super(…)steht in Java als erste Anweisung. Er übernimmt die Parameter, die die Oberklasse braucht; danach setzt die Unterklasse ihre eigenen Attribute. Bis Java 24 ist diese Reihenfolge Pflicht. Seit Java 25 (JEP 513, „Flexible Constructor Bodies“) dürfen vorsuper(…)Anweisungen stehen, die nicht aufthiszugreifen, etwa eine Prüfung der Parameter; eigenen Attributen darf dort höchstens ein Wert zugewiesen werden. Üblich und in Aufgaben erwartet bleibtsuper(…)am Anfang.Die privaten Attribute der Oberklasse (
isbn,titel) könnte die Unterklasse gar nicht direkt setzen. Auch deshalb führt kein Weg ansuper(…)vorbei.Hat die Oberklasse einen parameterlosen Konstruktor, ruft Java ihn automatisch auf. Hat sie nur Konstruktoren mit Parametern, ist
super(…)Pflicht.In C# steht der Aufruf im Kopf (
public Fachbuch(…) : base(isbn, titel, seiten)), in Python heißt ersuper().__init__(isbn, titel, seiten).
Auch eine abstrakte Klasse hat einen Konstruktor, obwohl man von ihr keine Objekte erzeugen kann. Er wird über super(…) aus den Unterklassen aufgerufen und initialisiert die gemeinsamen Attribute.
Geschützte Attribute (protected)
Ist ein Attribut der Oberklasse geschützt (#, protected), dürfen Methoden der Unterklasse direkt darauf zugreifen, zum Beispiel return "Fachbuch " + isbn, falls isbn geschützt wäre. Fremde Klassen dürfen das nicht (in Java gilt protected zusätzlich innerhalb desselben Pakets). Ist das Attribut privat, nutzt auch die Unterklasse den Getter. Mehr dazu im Artikel zur Vererbung.
Referenzen statt Kopien: Was bei der Übergabe passiert
Eine Variable vom Typ einer Klasse enthält nicht das Objekt selbst, sondern eine Referenz darauf. Das hat Folgen, die in Aufgaben gern abgefragt werden.
Zwei Variablen, ein Objekt
Buch a = new Buch("978-3-16-148410-0", "Netzwerke für Einsteiger", 256)
Buch b = a
b.setTitel("Netzwerke kompakt")
// a.getTitel() liefert jetzt "Netzwerke kompakt"
Buch b = a kopiert nur die Referenz. Beide Variablen zeigen auf dasselbe Objekt (Aliasing). Eine Kopie entsteht nur mit new. Aus demselben Grund vergleicht a == b bei Objekten, ob es dasselbe Objekt ist, und nicht, ob die Inhalte gleich sind; dafür gibt es equals.
Objekte und Arrays an Methoden übergeben
Übergibst Du ein Objekt oder ein Array an eine Methode, bekommt die Methode eine Kopie der Referenz. Sie arbeitet also mit demselben Objekt wie der Aufrufer. Ändert sie das Objekt (oder die Elemente des Arrays), sieht der Aufrufer die Änderung nach dem Aufruf:
+ messwerteEintragen(Real[] ziel) {
for (Integer i = 0; i < ziel.length; i++) {
ziel[i] = sensor.lesen()
}
}
Real[] werte = new Real[10]
messwerteEintragen(werte)
// werte enthält jetzt zehn Messwerte, obwohl die Methode nichts zurückgibt
Das ist ein häufiges Muster bei Schnittstellen zu Geräten und Bibliotheken: Der Aufrufer stellt den Speicher bereit, die Methode füllt ihn. In Lösungen heißt das oft „Übergabe als Referenz“ (call by reference).
Bei einfachen Datentypen (Integer, Real, Boolean, in Java int, double, boolean) wird dagegen der Wert kopiert (call by value). Ändert die Methode ihren Parameter, bleibt die Variable des Aufrufers unverändert:
+ erhoehe(Integer n) {
n = n + 1 // ändert nur die lokale Kopie
}
Integer anzahl = 5
erhoehe(anzahl)
// anzahl ist weiterhin 5
Genau genommen übergibt Java immer eine Kopie: bei einfachen Typen die Kopie des Werts, bei Objekten die Kopie der Referenz (manchmal call by sharing genannt). Das erklärt einen Sonderfall: Weist die Methode ihrem Parameter ein neues Objekt zu (ziel = new Real[10]), zeigt nur die lokale Kopie der Referenz auf das neue Array; das Array des Aufrufers bleibt unverändert. Zeichenketten (String) und Wrapper wie Integer sind in Java unveränderlich und verhalten sich deshalb wie einfache Werte. Echte Referenzparameter, bei denen auch eine neue Zuweisung beim Aufrufer ankommt, gibt es z. B. in C# (ref) und C++ (&).
Wer das veranschaulichen will, zeichnet ein Objektdiagramm: Die Variablen sind Pfeile, die Objekte Kästen. Zwei Pfeile auf denselben Kasten bedeuten ein Objekt.
Die ganze Klasse in Java und Python
Zum Vergleich die Klasse Buch vollständig in zwei Programmiersprachen:
public class Buch {
private final String isbn;
private String titel;
private int seiten;
private boolean verfuegbar = true;
public Buch(String isbn, String titel, int seiten) {
this.isbn = isbn;
this.titel = titel;
setSeiten(seiten);
}
public String getIsbn() { return isbn; }
public String getTitel() { return titel; }
public void setTitel(String titel) { this.titel = titel; }
public int getSeiten() { return seiten; }
public void setSeiten(int seiten) {
if (seiten > 0) {
this.seiten = seiten;
}
}
public boolean istVerfuegbar() { return verfuegbar; }
public void setVerfuegbar(boolean verfuegbar) { this.verfuegbar = verfuegbar; }
}
public class Fachbuch extends Buch {
private String fachgebiet;
public Fachbuch(String isbn, String titel, int seiten, String fachgebiet) {
super(isbn, titel, seiten);
this.fachgebiet = fachgebiet;
}
public String getFachgebiet() { return fachgebiet; }
}
// Verwendung
Buch b = new Fachbuch("978-3-446-47126-2", "Datenbanken", 412, "Informatik");
b.setSeiten(-3); // wird ignoriert
System.out.println(b.getSeiten()); // 412Typische Fehler
| Fehler | Beispiel | Richtig |
|---|---|---|
| Konstruktor mit Rückgabetyp | public void Buch(String isbn) |
public Buch(String isbn) |
this vergessen, Parameter verdeckt Attribut |
titel = titel |
this.titel = titel |
| Getter mit Parameter oder ohne Rückgabe | + getTitel(String titel) |
+ getTitel(): String { return titel } |
| Setter gibt den Wert zurück oder hat keinen Parameter | + setTitel(): String |
+ setTitel(String titel) |
| Getter liefert den Parameter statt des Attributs | + getTitel(String titel) { return titel } |
ohne Parameter, return this.titel |
Objekt ohne new erzeugt (Java, C#) |
Buch b = Buch(…) |
Buch b = new Buch(…) |
| Direkter Zugriff auf private Attribute von außen | b.titel = "…", a.leser.name |
b.setTitel("…"), a.getLeser().getName() |
| Argumente in falscher Reihenfolge | new Buch(256, "Netzwerke", "978-…") |
Reihenfolge wie im Konstruktor |
super(…) fehlt oder steht nicht am Anfang |
super(…) fehlt, obwohl Buch nur Konstruktoren mit Parametern hat; setSeiten(…) vor super(…) |
super(…) als erste Anweisung (seit Java 25 dürfen davor nur Anweisungen ohne Zugriff auf this stehen) |
| Prüfung im Konstruktor umgangen | this.seiten = seiten trotz Setter mit Prüfung |
setSeiten(seiten) aufrufen |
this weitergegeben, bevor die Attribute gesetzt sind |
leser.ausleiheHinzufuegen(this) als erste Zeile |
erst Attribute setzen, dann anmelden |
Methode auf null aufgerufen |
anlage.hinzufuegen(this) ohne Prüfung |
if (anlage != null) { … } |
So gehst Du bei Aufgaben vor
Vorgaben genau lesen: Welche Sichtbarkeit sollen Attribute und Methoden haben? Für welche Attribute sind Getter oder Setter verlangt? Sind Datentypen vorgegeben oder sollst Du sinnvolle wählen?
Erst der Kopf, dann der Rumpf: Sichtbarkeit, Name, Parameter mit Typen, Rückgabetyp. Ein vollständiger Methodenkopf zeigt schon, dass Du die Schnittstelle verstanden hast.
Konstruktor: Klassenname, kein Rückgabetyp, jedes Attribut aus einem Parameter oder Startwert setzen, bei Unterklassen zuerst
super(…). Soll sich das Objekt irgendwo anmelden, zuletztandereKlasse.anmelden(this).Objekterzeugung: Konstruktor im Klassendiagramm nachsehen, Argumente in der richtigen Reihenfolge,
newnicht vergessen, Ergebnis einer Variablen passenden Typs zuweisen oder direkt übergeben.Erläuterungen: Fachbegriffe benutzen (Kapselung, Konstruktor, Referenz, Wert, Vererbung) und den Mechanismus beschreiben, nicht nur das Ergebnis („Das Array wird als Referenz übergeben; die Methode schreibt in dasselbe Array, das der Aufrufer angelegt hat.“).
Schreibweise: Jede eindeutige Pseudocode-Form ist richtig. Wichtig sind vollständige Köpfe und die richtige Logik, nicht Semikolons.
Leitfragen
- Wie übersetzt Du Attribute, Konstruktor, Getter und Setter aus einem UML-Klassendiagramm in Pseudocode, und welche sinnvollen Datentypen wählst Du für Nummern, Beträge und Datumswerte?
Jedes Element hat eine feste Entsprechung: -titel: String wird zu - String titel, der Konstruktor +Buch(…) zu + Buch(String isbn, …) { … } ohne Rückgabetyp, +getTitel(): String zu einer Methode mit Rückgabe, und Assoziationen werden zu Attributen, die die Referenz speichern. Nummern mit führenden Nullen oder Bindestrichen (ISBN, Postleitzahl) sind String, Geldbeträge Real (in echter Software besser BigDecimal), Datumswerte ein Datumstyp wie Datum bzw. LocalDate – siehe Abschnitt „Vom Klassendiagramm zum Klassengerüst“.
- Woran erkennst Du einen Konstruktor, warum hat er keinen Rückgabetyp, und wozu brauchst Du
thisdarin?
Ein Konstruktor heißt wie die Klasse (in Python __init__), ist meist öffentlich und hat keinen Rückgabetyp, auch nicht void, weil das neue Objekt von new geliefert wird – mit void wäre es eine gewöhnliche Methode. this brauchst Du, weil gleichnamige Parameter die Attribute verdecken: this.titel = titel setzt das Attribut, titel = titel weist den Parameter sich selbst zu – siehe Abschnitt „Der Konstruktor: Objekte in einen gültigen Anfangszustand bringen“ und Abschnitt „Wozu this?“.
- Wie sind Getter und Setter aufgebaut, wie schreibst Du einen Setter mit Gültigkeitsprüfung, und wann bekommt ein Attribut nur einen Getter?
Ein Getter heißt get… (bei Boolean oft is…), hat keine Parameter und gibt das Attribut zurück; ein Setter heißt set…, hat genau einen Parameter vom Attributtyp und keine Rückgabe. Ein Setter mit Prüfung setzt den Wert nur, wenn er gültig ist (z. B. if (seiten > 0)), begrenzt ihn oder meldet Erfolg. Nur einen Getter bekommt ein Attribut, das sich nach dem Erzeugen nicht mehr ändern darf, etwa die ISBN – siehe Abschnitt „Getter und Setter“ und Abschnitt „Nicht jedes Attribut braucht einen Setter“.
- Warum sind Attribute privat, und wie greifst Du von außen auf die Daten eines Objekts zu?
Private Attribute setzen das Prinzip der Kapselung um: Setter verhindern ungültige Zustände, die Klasse kontrolliert, was gelesen und geändert werden darf, und die interne Darstellung bleibt änderbar. Von außen greifst Du nur über Methoden zu, also ausleihe.getLeser().getName() statt ausleihe.leser.name – siehe Abschnitt „Warum nicht einfach öffentliche Attribute? Kapselung“.
- Wie erzeugst Du ein Objekt, übergibst es einem anderen Objekt und meldest ein Objekt im Konstruktor mit
thisan?
Du erzeugst ein Objekt mit new und den Argumenten in der Reihenfolge des Konstruktors, z. B. Buch b = new Buch(…), und übergibst die Referenz einer Methode eines anderen Objekts (messreihe.hinzufuegen(m)). Im Konstruktor setzt Du zuerst die eigenen Attribute und meldest das neue Objekt dann mit leser.ausleiheHinzufuegen(this) an – siehe Abschnitt „Objekte erzeugen und verwenden“ und Abschnitt „Objekte, die sich selbst bekannt machen: this als Argument“.
- Wie sieht der Konstruktor einer Unterklasse aus, und welche Rolle spielen
super(…)undprotected?
Konstruktoren werden nicht vererbt; der Konstruktor der Unterklasse ruft als erste Anweisung super(…) mit den Parametern der Oberklasse auf und setzt danach seine eigenen Attribute. Private Attribute der Oberklasse kann die Unterklasse nicht direkt setzen; ist ein Attribut protected (#), dürfen Methoden der Unterklasse direkt darauf zugreifen, fremde Klassen nicht – siehe Abschnitt „Konstruktoren in Unterklassen: super(…)“ und Abschnitt „Geschützte Attribute (protected)“.
- Wie vergibst Du fortlaufende Nummern mit einem Klassenattribut, und wozu dient
this(…)?
Ein Klassenattribut (static, im Diagramm unterstrichen) wie naechsteNummer = 1001 gibt es nur einmal für alle Objekte; der Konstruktor weist leserNr = naechsteNummer zu und erhöht danach den Zähler. this(…) ruft in einem überladenen Konstruktor einen anderen Konstruktor derselben Klasse auf, damit gemeinsamer Code nicht doppelt steht; in Java steht es als erste Anweisung (bis Java 24 Pflicht, seit Java 25 dürfen davor Anweisungen ohne Zugriff auf this stehen) – siehe Abschnitt „Klassenattribute: fortlaufende Nummern vergeben“ und Abschnitt „Mehrere Konstruktoren: Überladung und this(…)“.
- Warum kann eine Methode ohne Rückgabewert Daten an den Aufrufer liefern, und warum bleibt eine übergebene Zahl nach dem Aufruf unverändert?
Bei Objekten und Arrays erhält die Methode eine Kopie der Referenz und arbeitet mit demselben Objekt wie der Aufrufer, sodass er Änderungen nach dem Aufruf sieht. Bei einfachen Datentypen wird dagegen der Wert kopiert (call by value); ändert die Methode ihren Parameter, bleibt die Variable des Aufrufers unverändert – siehe Abschnitt „Objekte und Arrays an Methoden übergeben“.
Links und weitere Informationen
Grundlagen des UML-Klassendiagramms und Sichtbarkeitsmodifikatoren
Klassenbeziehungen: Vererbung, Abstrakte Klassen und Interfaces und Polymorphie
Objektorientierte Algorithmen: Methoden über Objektlisten (Getter, Konstruktoren und Listen in größeren Methoden nutzen)
Objektinstanzen im UML-Objektdiagramm darstellen und Entwurfsmuster Beobachter
Objektorientierte Programmierung mit Python und Kapselung in Python
als Nachschlagewerk: The Java Tutorials: Classes and Objects (Konstruktoren,
this, Klassenattribute, Übergabe von Argumenten)Primärquelle: JEP 513: Flexible Constructor Bodies (Anweisungen vor
super(…)undthis(…)seit Java 25)Primärquelle: OMG Unified Modeling Language (UML) 2.5.1 (Abschnitt 9.6 Operationen, 11.4 Klassen, 21 Primitivtypen)
Hinweis zur Nachnutzung als Open Educational Resource (OER)
Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “Klassen implementieren: Konstruktor, Getter, Setter und Objekterzeugung” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/klassen-implementieren veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/programmierung/programmiergrundlagen. Erstellt mit KI-Assistenz; von mir umfassend redaktionell überarbeitet, geprüft und freigegeben. Stand: 28.10.2026.
[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]
