MINT lernen

Typische Fehler

Zwölf Fehler, die beim Umstieg von Python auf Java immer wieder passieren — mit falschem und richtigem Code und der echten Meldung von javac oder java.

Hier sind die 12 häufigsten Fehler aus dem ganzen Kapitel — vom fehlenden Semikolon bis zur Suche in der Liste. Jede Karte zeigt den fehlerhaften Code, die Meldung, die du dann wirklich bekommst, und den korrigierten Code. Lies die Meldungen genau: Wer sie einmal verstanden hat, findet den Fehler in Sekunden.

!

Die 12 häufigsten Fehler

1Semikolon am Zeilenende vergessen

falsch
public class Wecker {
    public static void main(String[] args) {
        System.out.println("Aufstehen!")
        System.out.println("Noch 5 Minuten ...");
    }
}
Wecker.java:3: error: ';' expected
        System.out.println("Aufstehen!")
                                        ^
1 error
richtig
        System.out.println("Aufstehen!");
        System.out.println("Noch 5 Minuten ...");
javac zeigt mit ^ genau auf die Stelle, an der das Semikolon fehlt.

So wird es oft gemacht: Aus Python gewohnt, endet die Zeile einfach mit dem Zeilenumbruch. In Java übersetzt javac dann gar nichts — es entsteht keine .class-Datei, das Programm startet nicht.

Richtig ist: Jede Anweisung endet mit ;. Nach Kopfzeilen mit { (Klasse, Methode, if, for) steht dagegen kein Semikolon.

Anweisung → Semikolon. Block-Kopf mit { → kein Semikolon.

2Ganzzahldivision übersehen — der Schnitt ist zu klein

falsch
int strecke = 23;   // km
int tage = 4;
double schnitt = strecke / tage;
System.out.println(schnitt);
5.0
richtig
double schnitt = (double) strecke / tage;
System.out.println(schnitt);
5.75
Der double-Typ links rettet nichts mehr: gerechnet wird rechts.

So wird es oft gemacht: „Das Ergebnis ist double, also kommen Nachkommastellen heraus.“ Aber 23 / 4 sind zwei int-Werte — Java rechnet ganzzahlig, schneidet ab und erhält 5. Erst danach wird daraus 5.0.

Richtig ist: Mindestens ein Operand muss double sein, z. B. mit (double) strecke / tage oder strecke / 4.0. Dann ist das Ergebnis 5,75 km pro Tag.

int / int ergibt immer int — egal, wohin das Ergebnis gespeichert wird.

3Kommazahl in einer int-Variablen gespeichert

falsch
int preis = 8.50;   // Kinokarte
Kino.java:3: error: incompatible types: possible lossy conversion from double to int
        int preis = 8.50;
                    ^
1 error
richtig
double preis = 8.50;
„lossy conversion“ heißt: Beim Umwandeln ginge etwas verloren.

So wird es oft gemacht: In Python bekommt eine Variable jeden Wert. In Java steht der Typ fest: In int passen nur ganze Zahlen. javac verweigert die Übersetzung, weil die 0,50 € verloren gingen.

Richtig ist: Für Kommazahlen double wählen. Umgekehrt klappt es automatisch: double d = 8; wird zu 8.0. Wer bewusst abschneiden will, schreibt (int) 8.50 und erhält 8.

Erst den Typ überlegen, dann die Variable anlegen — der Compiler prüft ihn vor dem Start.

4Texte mit == statt mit equals verglichen

falsch
Scanner sc = new Scanner(System.in);
String antwort = sc.nextLine();       // Eingabe: ja
System.out.println(antwort == "ja");
false
richtig
System.out.println(antwort.equals("ja"));
true
Gleicher Text, aber zwei verschiedene String-Objekte.

So wird es oft gemacht: In Python vergleicht == den Text. In Java prüft == bei Strings nur, ob beide Variablen auf dasselbe Objekt zeigen. Die Eingabe ist ein neues Objekt — das Ergebnis ist false, obwohl „ja“ eingetippt wurde.

Richtig ist: antwort.equals("ja") vergleicht Zeichen für Zeichen. == bleibt für int, double, char und boolean richtig.

Zahlen mit ==, Texte mit .equals(…).

5return fehlt — obwohl alle Fälle abgedeckt scheinen

falsch
public static String bewertung(int prozent) {
    if (prozent >= 50) {
        return "bestanden";
    } else if (prozent < 50) {
        return "nicht bestanden";
    }
}
Test.java:8: error: missing return statement
    }
    ^
1 error
richtig
public static String bewertung(int prozent) {
    if (prozent >= 50) {
        return "bestanden";
    } else {
        return "nicht bestanden";
    }
}
javac prüft nicht, ob die Bedingungen zusammen alles abdecken.

So wird es oft gemacht: Du weißt: Eine Zahl ist entweder ≥ 50 oder < 50. Der Compiler rechnet das aber nicht nach. Für ihn gibt es einen Weg, auf dem die Methode ohne return endet — und eine Methode mit Rückgabetyp String muss auf jedem Weg etwas zurückgeben.

Richtig ist: Den letzten Fall mit else schreiben oder nach dem if noch ein return ergänzen.

Rückgabetyp ≠ void → jeder mögliche Weg durch die Methode endet mit return.

6Typen aus dem Klassendiagramm in Java übernommen

Drache
  • - name: Zeichenkette
  • - leben: Ganzzahl
  • c Drache(name: Zeichenkette)
falsch
public class Drache {
    private String name;
    private Ganzzahl leben;
}
Drache.java:3: error: cannot find symbol
    private Ganzzahl leben;
            ^
  symbol:   class Ganzzahl
  location: class Drache
1 error
richtig
public class Drache {
    private String name;
    private int leben;
}
Im Diagramm steht der Typ hinten und deutsch, in Java vorne und englisch.

So wird es oft gemacht: Die Zeile - leben: Ganzzahl wird fast wörtlich abgeschrieben. Java kennt aber keinen Typ „Ganzzahl“ und sucht vergeblich nach einer Klasse mit diesem Namen.

Richtig ist: Übersetzen: Ganzzahl → int, Fließkommazahl → double, Wahrheitswert → boolean, Zeichen → char, Zeichenkette → String; - → private, + → public; der Typ wandert nach vorn.

- leben: Ganzzahl wird private int leben; — nie wörtlich abschreiben.

7Objekt ohne new erzeugt

falsch
Zug z = Zug("ICE", 8);
Main.java:3: error: cannot find symbol
        Zug z = Zug("ICE", 8);
                ^
  symbol:   method Zug(String,int)
  location: class Main
1 error
richtig
Zug z = new Zug("ICE", 8);
Ohne new sucht javac eine Methode namens Zug — und findet keine.

So wird es oft gemacht: In Python entsteht ein Objekt mit Zug("ICE", 8). In Java hält der Compiler das für den Aufruf einer Methode Zug(String,int), die es nicht gibt.

Richtig ist: new erzeugt das Objekt und ruft dabei den Konstruktor auf. Links steht der Typ der Variablen, hier ebenfalls Zug.

Neues Objekt = Typ name = new Klasse(werte);

8this fehlt — der Konstruktor speichert nichts

falsch
public Planet(String name, double masse) {
    name = name;
    masse = masse;
}
// new Planet("Mars", 0.64) → getName(), getMasse()
null 0.0
richtig
public Planet(String name, double masse) {
    this.name = name;
    this.masse = masse;
}
Mars 0.64
Kein Fehler beim Übersetzen — aber die Attribute behalten ihre Standardwerte.

So wird es oft gemacht: Parameter und Attribut heißen gleich. In name = name; ist dann beide Male der Parameter gemeint — er wird sich selbst zugewiesen. Das Attribut bleibt bei null bzw. 0.0.

Richtig ist: this.name ist das Attribut dieses Objekts, name allein der Parameter. Also this.name = name;.

Gleicher Name bei Parameter und Attribut → links this. davor.

9Privates Attribut direkt von außen gesetzt

falsch
Thermostat t = new Thermostat();   // startet mit 20
t.temperatur = 50;
Main.java:4: error: temperatur has private access in Thermostat
        t.temperatur = 50;
         ^
1 error
richtig
// in der Klasse Thermostat:
public void setTemperatur(int temperatur) {
    if (temperatur >= 5 && temperatur <= 30) {
        this.temperatur = temperatur;
    }
}
// außerhalb:
t.setTemperatur(50);   // wird abgewiesen, bleibt 20
private schützt das Attribut, der Setter prüft den neuen Wert.

So wird es oft gemacht: Aus einer anderen Klasse wird das Attribut direkt gesetzt. Weil es private ist, bricht javac ab. Wer daraufhin einfach public schreibt, erlaubt auch unsinnige Werte wie 50 °C im Wohnzimmer.

Richtig ist: Attribut privat lassen und einen Setter anbieten, der nur gültige Werte übernimmt. Ungültige Werte werden abgewiesen, nicht stillschweigend auf die Grenze gesetzt.

Attribute private, Zugriff nur über getX() und einen prüfenden setX(…).

10Schleife läuft bis length — einen Schritt zu weit

falsch
int[] tore = {2, 0, 3, 1, 4};
int summe = 0;
for (int i = 0; i <= tore.length; i++) {
    summe = summe + tore[i];
}
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
	at Main.main(Main.java:6)
richtig
for (int i = 0; i < tore.length; i++) {
    summe = summe + tore[i];
}
// summe = 10
Übersetzen klappt — der Fehler kommt erst beim Programmlauf.

So wird es oft gemacht: Das Array hat 5 Plätze, also soll die Schleife „bis 5“ laufen. Die Indizes gehen aber nur von 0 bis 4. Beim Zugriff auf tore[5] bricht die JVM mit einem Laufzeitfehler ab.

Richtig ist: i < tore.length. Der letzte gültige Index ist immer length − 1. Bei einer ArrayList gilt dasselbe mit size().

Indizes laufen von 0 bis length − 1 — in der Bedingung steht <, nicht <=.

11super(…) im Konstruktor der Unterklasse vergessen

falsch
public class Gitarre extends Instrument {
    private int saiten;

    public Gitarre(String name, int saiten) {
        this.saiten = saiten;
    }
}
// Instrument hat nur den Konstruktor Instrument(String name)
Gitarre.java:7: error: constructor Instrument in class Instrument cannot be applied to given types;
    public Gitarre(String name, int saiten) {
                                            ^
  required: String
  found:    no arguments
  reason: actual and formal argument lists differ in length
1 error
richtig
    public Gitarre(String name, int saiten) {
        super(name);
        this.saiten = saiten;
    }
Ohne super(…) versucht Java den Konstruktor der Oberklasse ohne Parameter.

So wird es oft gemacht: Die Gitarre setzt nur ihr neues Attribut. Den geerbten Teil muss aber der Konstruktor der Oberklasse füllen. Fehlt super(…), ruft Java heimlich super() ohne Werte auf — und den gibt es hier nicht.

Richtig ist: Als erste Anweisung super(name);, danach die eigenen Attribute. An name kommt die Gitarre ohnehin nicht direkt heran, weil es in Instrument private ist.

Unterklassen-Konstruktor: zuerst super(…), dann this.neu = neu;

12Die Suche gibt nach dem ersten Vergleich auf

falsch
public Spieler suche(String name) {
    for (Spieler s : kader) {
        if (s.getName().equals(name)) {
            return s;
        } else {
            return null;
        }
    }
    return null;
}
// kader: Ben, Lina → suche("Lina").getName()
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Spieler.getName()" because the return value of "Team.suche(String)" is null
	at Main.main(Main.java:7)
richtig
public Spieler suche(String name) {
    for (Spieler s : kader) {
        if (s.getName().equals(name)) {
            return s;
        }
    }
    return null;   // erst NACH der Schleife: niemand gefunden
}
return beendet die Methode sofort — auch mitten in der Schleife.

So wird es oft gemacht: „Wenn der Name nicht passt, gib null zurück.“ Dann wird nur der erste Spieler geprüft: Ben passt nicht, die Methode endet mit null, Lina wird nie angeschaut. Der Aufruf getName() an null führt zur NullPointerException.

Richtig ist: Im Schleifenrumpf nur bei einem Treffer return. Erst wenn alle Elemente geprüft sind, steht fest, dass niemand passt — also return null; nach der Schleife.

„Gefunden“ darf die Schleife beenden, „nicht gefunden“ erst ihr Ende.