Hier sind die 12 häufigsten Fehler, die in Klausuren zur Objektorientierung vorkommen. Jede Karte zeigt links den fehlerhaften und den korrigierten Code, rechts die Erklärung. Wer einen Fehler kennt, macht ihn seltener.
Die 12 häufigsten Fehler
1Parameter überdeckt Attribut — this fehlt
// falsch
public Pflanze(String art, int hoehe) {
art = art;
hoehe = hoehe;
}
// richtig
public Pflanze(String art, int hoehe) {
this.art = art;
this.hoehe = hoehe;
}
So wird es oft gemacht: Die Parameter heißen wie die Attribute, im Rumpf steht art = art;. Das übersetzt ohne Fehler — aber art bezeichnet dort beide Male den Parameter. Das Attribut bleibt null, hoehe bleibt 0.
Richtig ist: this.art = art; — links das Attribut des Objekts, rechts der Parameter. Alternativ bekommen die Parameter andere Namen, z. B. neueArt.
Heißen Parameter und Attribut gleich, gehört vor das Attribut ein this..
2Konstruktor mit Rückgabetyp void
// falsch
public void Getraenkeautomat(int flaschen) {
this.flaschen = flaschen;
}
// new Getraenkeautomat(20) → Übersetzungsfehler
// richtig
public Getraenkeautomat(int flaschen) {
this.flaschen = flaschen;
}
So wird es oft gemacht: „Der Konstruktor gibt nichts zurück, also schreibe ich void.“ Java hält void Getraenkeautomat(int flaschen) dann für eine normale Methode. Der Aufruf new Getraenkeautomat(20) findet keinen passenden Konstruktor.
Richtig ist: Der Konstruktor hat überhaupt keinen Rückgabetyp und heißt exakt wie die Klasse. Im Diagramm steht er mit c: c Getraenkeautomat(flaschen: Ganzzahl).
Konstruktor: Name der Klasse, kein Rückgabetyp — auch nicht void.
3Zeichenketten mit == verglichen
// falsch
public boolean istRichtig(String antwort) {
return antwort == loesung;
}
// richtig
public boolean istRichtig(String antwort) {
return antwort.equals(loesung);
}
So wird es oft gemacht: In der Klasse Frage eines Quiz wird die eingegebene Antwort mit == geprüft. Kommt die Antwort aus einer Eingabe, ist sie ein anderes String-Objekt — das Ergebnis ist false, obwohl „Paris“ und „Paris“ gleich aussehen.
Richtig ist: antwort.equals(loesung) vergleicht die Zeichen. == prüft nur, ob beide Variablen auf dasselbe Objekt zeigen; dass es in Tests manchmal zufällig klappt, macht es nicht richtig.
Zeichenketten und andere Objekte inhaltlich immer mit equals vergleichen.
4Reihung angelegt, aber keine Objekte erzeugt
// falsch
Fisch[] becken = new Fisch[5];
becken[0].fuettern(); // NullPointerException
// richtig
Fisch[] becken = new Fisch[5];
for (int i = 0; i < becken.length; i++) {
becken[i] = new Fisch("Guppy");
}
becken[0].fuettern();
So wird es oft gemacht: „Mit new Fisch[5] habe ich fünf Fische.“ Tatsächlich enthält jeder Platz null. Der erste Methodenaufruf bricht mit einer NullPointerException ab.
Richtig ist: Jeder Platz bekommt sein eigenes Objekt mit new Fisch("Guppy"). Sind nicht alle Plätze belegt, vor jedem Aufruf mit if (becken[i] != null) prüfen.
Ein new für die Reihung — und ein new für jedes Objekt darin.
5Referenz-Attribut nie mit new belegt
// falsch
public class Konzert {
private Ticket vip;
public int vipPreis() {
return vip.getPreis(); // vip ist null
}
}
// richtig
public Konzert() {
vip = new Ticket(89);
}
So wird es oft gemacht: Das Attribut private Ticket vip; wird deklariert und später benutzt. Weil es nie ein Objekt bekommt, steht darin null — vipPreis() endet mit einer NullPointerException.
Richtig ist: Der Konstruktor von Konzert erzeugt das Ticket mit new oder bekommt es als Parameter übergeben. Erst danach zeigt vip auf ein Objekt.
Eine Variable vom Objekttyp ist nur ein Zeiger — das Objekt selbst entsteht erst mit new.
6Getter ohne return, Setter ohne Prüfung
// falsch public void getSchritte() { System.out.println(schritte); } public void setPuls(int puls) { this.puls = puls; // auch -20 }
// richtig
public int getSchritte() {
return schritte;
}
public void setPuls(int puls) {
if (puls >= 30 && puls <= 220) {
this.puls = puls;
}
}
So wird es oft gemacht: Beim Fitnesstracker gibt getSchritte() den Wert auf dem Bildschirm aus, statt ihn zurückzugeben — int s = f.getSchritte(); übersetzt dann nicht. Der Setter übernimmt jeden Wert, auch einen Puls von −20.
Richtig ist: Der Getter hat den Attributtyp als Rückgabetyp und endet mit return. Der Setter übernimmt nur plausible Werte; sonst bleibt der alte Zustand erhalten.
Getter: return, nicht println. Setter: erst prüfen, dann zuweisen.
7Attribut öffentlich statt privat
// falsch
public class Stromzaehler {
public int stand;
}
// von außen: z.stand = -500;
// richtig
public class Stromzaehler {
private int stand;
public void verbrauchen(int kWh) {
if (kWh > 0) {
stand = stand + kWh;
}
}
}
So wird es oft gemacht: „Mit public komme ich leichter an den Wert.“ Dann kann jede andere Klasse den Zählerstand zurückdrehen oder negativ setzen — die Datenkapselung ist aufgehoben.
Richtig ist: Attribute sind private (im Diagramm -). Geändert wird nur über Methoden, die den Wert prüfen; ein Zählerstand bekommt sinnvollerweise gar keinen Setter.
Attribute privat, Methoden öffentlich — Zugriff nur über Methoden.
8substring-Ende als inklusive gedacht
// falsch: gesucht ist "BER"
String strecke = "HAN-BER-07";
String ziel = strecke.substring(4, 6); // "BE"
// richtig String strecke = "HAN-BER-07"; String ziel = strecke.substring(4, 7); // "BER"
So wird es oft gemacht: Im Streckencode eines Zuges steht das Ziel an den Stellen 4 bis 6, also wird substring(4, 6) geschrieben. Das liefert nur "BE".
Richtig ist: substring(a, b) nimmt die Zeichen von Index a bis ausschließlich b. Die Länge des Ergebnisses ist b − a, hier 3.
Ende exklusiv: substring(a, b) liefert b − a Zeichen, gezählt ab Index 0.
9Notation im Klassendiagramm falsch
- - int datenvolumen
- Handyvertrag(tarif: Zeichenkette)
- + buchen(gb: Ganzzahl): void
- - datenvolumen: Ganzzahl
- c Handyvertrag(tarif: Zeichenkette)
- + buchen(gb: Ganzzahl)
So wird es oft gemacht: Java-Schreibweise im Diagramm (int vor dem Namen), Konstruktor ohne c und bei einem Auftrag ein Rückgabetyp void.
Richtig ist: Erst der Name, dann : Typ mit deutschem Typnamen; Konstruktor mit vorangestelltem c; ein Auftrag steht ganz ohne Rückgabetyp.
Diagramm: - name: Typ, c Klasse(p: Typ), Rückgabetyp nur bei Anfragen.
10Klasse und Objekt verwechselt
- - name: Zeichenkette
- - alter: Ganzzahl
- + impfen()
- name = "Bello"
- alter = 4
- geimpft = true
So wird es oft gemacht: Verlangt ist die Objektkarte des Hundes Bello in der Tierarztpraxis. Gezeichnet wird eine Karte mit Datentypen und Methoden — also eigentlich die Klasse Tier. Ebenso falsch: „Tier ist ein Objekt, Bello ist ein Attribut.“
Richtig ist: Tier ist die Klasse (der Bauplan), Bello ist ein Objekt dieser Klasse. Seine Objektkarte hat den unterstrichenen Kopf t1: Tier und darunter die aktuellen Attributwerte.
Klassenkarte: Typen und Methoden. Objektkarte: konkrete Werte, sonst nichts.
11Zuweisung für eine Kopie gehalten
// falsch: Kopie für nächste Woche?
Termin kopie = original;
kopie.setTag(14);
// original.getTag() ist jetzt auch 14
// richtig: neues Objekt Termin kopie = new Termin(original.getTitel(), 14);
So wird es oft gemacht: In einer Kalender-App soll ein Termin für die nächste Woche kopiert werden. kopie = original kopiert aber nur die Referenz. Danach zeigen beide Variablen auf denselben Termin, und der ursprüngliche Tag ist verloren.
Richtig ist: Eine echte Kopie ist ein neues Objekt mit new, das die Werte des Originals über Getter übernimmt. Nur bei primitiven Typen wie int kopiert die Zuweisung den Wert.
Zuweisung bei Objekten = zweiter Name für dasselbe Objekt (Alias).
12Methode ohne Objekt aufgerufen
// falsch
Teilnehmer.bezahlen(50);
// richtig
Teilnehmer t = new Teilnehmer("Mia");
t.bezahlen(50);
So wird es oft gemacht: Bei der Klassenfahrt-Verwaltung wird bezahlen direkt am Klassennamen aufgerufen oder ganz ohne Punkt. Der Übersetzer meldet einen Fehler, denn die Klasse selbst ist kein Teilnehmer mit eigenem Betrag.
Richtig ist: Erst ein Objekt erzeugen, dann die Methode an diesem Objekt aufrufen: objekt.methode(argumente). Innerhalb der eigenen Klasse darf das Objekt weggelassen werden, weil dann this gemeint ist.
Punktnotation: links ein Objekt, rechts seine Methode.
