Layer 8Ehrlicher Tech-Support
SoftwareSchweregrad: AnfängerfehlerError: Could not find or load main class

Java: Could not find or load main class

java findet die Startklasse nicht, obwohl die .class-Datei daneben liegt. Gefragt sind voll qualifizierter Klassenname und passender Klassenpfad.

Aufwand:
ca. 10 Min.
Voraussetzung:
Ein Terminal mit java und javac (JDK), Die Quelldatei oder das JAR, das gestartet werden soll, Bei JARs: jar oder unzip zum Blick ins Manifest
Gilt für:
JDK 25, Werkzeugreferenz java (docs.oracle.com, Java Platform, Standard Edition Tools Reference): Synopsis, -jar, --class-path/-classpath/-cp, CLASSPATH

Das Symptom

javac hat ohne Beanstandung übersetzt, die Datei Hallo.class liegt sichtbar im Ordner, und java antwortet: Could not find or load main class, gefolgt von dem, was man gerade getippt hat. Die Varianten sind zahlreich: java Hallo.class, java Hallo aus dem falschen Ordner, java -cp lib/tool.jar Hallo mit einer Klasse, die gar nicht im JAR liegt, java -jar app.jar mit einem Manifest ohne Startklasse, oder ein Klassenpfad, der unter Windows funktioniert und unter Linux nicht. Besonders beliebt ist der Fall, dass dasselbe Kommando auf dem Rechner des Kollegen läuft, weil dort eine Umgebungsvariable gesetzt ist, von der niemand mehr weiß.

Die „Layer 8“-Wahrheit

Der Launcher nimmt kein Dateiargument, sondern einen Namen. Die Werkzeugreferenz zu java legt fest: „By default, the first argument that isn’t an option of the java command is the fully qualified name of the class to be called.“ Voll qualifiziert bedeutet mit Paket, mit Punkten, ohne Endung. Aus dem Namen leitet java den Pfad ab, unter dem die Klassendatei relativ zu einem Eintrag des Klassenpfads liegen muss: de.firma.tool.Hallo wird zu de/firma/tool/Hallo.class. Wer eine Klasse mit package-Zeile aus ihrem Paketordner heraus mit bloßem Namen startet, verlangt eine Klasse ohne Paket, die es nicht gibt. Wer java Hallo.class tippt, verlangt eine Klasse namens class im Paket Hallo. Die Meldung ist in beiden Fällen wörtlich zutreffend.

Zweite Stellschraube ist der Klassenpfad, und auch da hat die Referenz eine klare Vorgabe: „If the class path option isn’t used and classpath isn’t set, then the user class path consists of the current directory (.).“ Das erklärt, warum der Start im richtigen Ordner ohne weitere Angabe klappt und ein Verzeichnis weiter nicht. Es erklärt auch die Rechnerabhängigkeit: Ist die Umgebungsvariable CLASSPATH gesetzt, gilt nicht mehr das aktuelle Verzeichnis, sondern deren Inhalt, es sei denn, -cp wird angegeben, denn „Specifying classpath overrides any setting of the CLASSPATH environment variable.“ Ein Rechner mit einer vor Jahren gesetzten CLASSPATH verhält sich darum anders als ein frisch aufgesetzter.

Dritte Stellschraube ist -jar. Mit dieser Option gilt laut Referenz: „When you use -jar, the specified JAR file is the source of all user classes, and other class path settings are ignored.“ Und: „The startup class must be indicated by the Main-Class manifest header in its manifest file.“ Was in -cp oder CLASSPATH steht, spielt dann keine Rolle mehr; die Startklasse muss im Manifest stehen und im JAR liegen. Ein JAR ohne Main-Class ist kein kaputtes JAR, es ist nur keines, das sich mit -jar starten lässt. Mit -cp app.jar und dem Klassennamen startet es trotzdem.

Dazu kommt die Plattformfrage beim Trennzeichen: Semikolon unter Windows, Doppelpunkt sonst, wie die Referenz bei –class-path beschreibt, plus der Platzhalter lib/*, der alle JAR-Dateien eines Ordners einschließt. Nichts davon ist versteckt; es steht alles in der Beschreibung des Kommandos, das die Meldung ausgibt. Die Meldung sagt nicht, dass die Klasse fehlt. Sie sagt, dass sie unter dem genannten Namen auf dem wirksamen Klassenpfad nicht gefunden wurde. Beides prüfen, in dieser Reihenfolge.

Die echte, verifizierte Lösung

1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:

  1. 1.

    Paket der Klasse feststellen

    Die erste Zeile mit package in der Quelldatei entscheidet über den Namen, den java verlangt. Ohne package-Zeile heißt die Klasse schlicht Hallo; mit package de.firma.tool heißt sie de.firma.tool.Hallo, und ihre Klassendatei gehört nach de/firma/tool/Hallo.class. javac -d legt diese Ordnerstruktur selbst an, wenn man ihm ein Zielverzeichnis gibt. Wer stattdessen die Klassendatei von Hand verschiebt oder im Paketordner stehend startet, verliert den Bezug zwischen Pfad und Name.

    grep -m1 '^package' Hallo.java
    javac -d out Hallo.java
    find out -name 'Hallo.class'
    
  2. 2.

    Mit Klassenpfad und voll qualifiziertem Namen starten

    Der Klassenpfad zeigt auf das Verzeichnis, unter dem die Paketordner beginnen, nicht auf den Paketordner selbst. Der Klassenname folgt mit Punkten, ohne Endung .class und ohne Pfadanteile. Steht die Klasse in keinem Paket, reicht der Name allein; der Klassenpfad ist dann der Ordner mit der Klassendatei, bei Start aus diesem Ordner laut Referenz das aktuelle Verzeichnis. Eine CLASSPATH-Variable, die etwas anderes enthält, wird durch -cp überschrieben, aber nur, wenn man -cp auch angibt.

    java -cp out de.firma.tool.Hallo
    echo "$CLASSPATH"
    
  3. 3.

    JAR-Manifest prüfen

    Bei -jar zählt nur das Manifest. Die Datei META-INF/MANIFEST.MF im JAR muss eine Zeile Main-Class mit dem voll qualifizierten Namen enthalten, und die Klasse muss im JAR unter dem passenden Pfad liegen. Beides lässt sich ohne Entpacken ansehen. Fehlt die Zeile, erzeugt man das JAR mit jar --main-class neu oder gibt die Klasse per -cp und Klassennamen an, statt -jar zu verwenden.

    unzip -p app.jar META-INF/MANIFEST.MF
    unzip -l app.jar | grep 'Hallo.class'
    java -cp app.jar de.firma.tool.Hallo
    
  4. 4.

    Trennzeichen und Platzhalter plattformgerecht setzen

    Mehrere Einträge im Klassenpfad werden unter Linux und macOS mit Doppelpunkt, unter Windows mit Semikolon getrennt. Der Platzhalter lib/* steht laut Referenz für alle .jar-Dateien im Ordner und erspart das Aufzählen; er gehört in Anführungszeichen, damit die Shell ihn nicht selbst auflöst. Skripte, die auf beiden Plattformen laufen sollen, bauen den Klassenpfad deshalb zur Laufzeit zusammen, statt ihn fest einzutragen.

    java -cp "out:lib/*" de.firma.tool.Hallo
    

Häufige Fragen (FAQ)

?Die Datei Hallo.class liegt im Ordner. Warum findet java sie nicht?

Weil java keinen Dateinamen erwartet, sondern einen Klassennamen. Die Werkzeugreferenz sagt: „By default, the first argument that isn't an option of the java command is the fully qualified name of the class to be called.“ Voll qualifiziert heißt mit Paket: Steht in der Quelldatei package de.firma.tool;, lautet der Name de.firma.tool.Hallo, und die Klassendatei muss unter de/firma/tool/Hallo.class relativ zu einem Eintrag des Klassenpfads liegen. Wer java Hallo.class tippt, verlangt eine Klasse namens class im Paket Hallo. Wer im Ordner de/firma/tool steht und java Hallo tippt, verlangt eine Klasse ohne Paket. Beides gibt es nicht.

?Was ist der Klassenpfad, wenn ich keinen angebe?

Das aktuelle Verzeichnis. Die Referenz formuliert es bei der Option --class-path: „If the class path option isn't used and classpath isn't set, then the user class path consists of the current directory (.).“ Sobald aber -cp oder --class-path angegeben ist, gilt nur noch diese Liste, und wer dort nur ein JAR nennt, hat das aktuelle Verzeichnis damit abgewählt. Dasselbe gilt für die Umgebungsvariable CLASSPATH: „Specifying classpath overrides any setting of the CLASSPATH environment variable.“ Eine vergessene CLASSPATH-Variable aus einer alten Installation ist eine klassische Ursache dafür, dass java auf einem Rechner die Klasse findet und auf dem anderen nicht.

?Mit -jar kommt dieselbe Meldung, obwohl -cp stimmt. Warum?

Weil -jar den Klassenpfad außer Kraft setzt. Die Referenz ist deutlich: „When you use -jar, the specified JAR file is the source of all user classes, and other class path settings are ignored.“ Die Startklasse kommt dann ausschließlich aus dem Manifest: „The startup class must be indicated by the Main-Class manifest header in its manifest file.“ Fehlt die Zeile Main-Class oder nennt sie eine Klasse, die nicht im JAR liegt, erscheint die Meldung, egal was in -cp oder CLASSPATH steht. Abhängigkeiten außerhalb des JARs müssen über den Class-Path-Eintrag des Manifests kommen, nicht über -cp.

?Unter Windows funktioniert mein Klassenpfad, unter Linux nicht.

Vermutlich steckt das Trennzeichen dazwischen. Die Referenz beschreibt für --class-path, dass unter Windows Semikolons die Einträge trennen und auf den anderen Plattformen ein Doppelpunkt. Ein aus einer Windows-Anleitung kopiertes lib/a.jar;lib/b.jar ist unter Linux ein einziger Eintrag mit Semikolon im Namen, und die Shell interpretiert das Semikolon obendrein als Befehlstrenner. Umgekehrt ist ein Doppelpunkt unter Windows Teil eines Laufwerksbuchstabens. Für ganze Verzeichnisse voller JARs nennt die Referenz die Kurzform lib/*, die alle .jar-Dateien des Ordners einschließt.

Belege

Diese Seite ist gegen die folgenden Primärquellen geprüft – Hersteller, Standard oder Gesetzestext. Wer es nicht glaubt, muss es nicht: Da steht, wo es nachzulesen ist.

Zuletzt geprüft am . Dokumentation altert – ein Datum daneben ist der einzige ehrliche Umgang damit.

Auch zu finden unter

Verwandte Fehler

SWLästigUnsupportedClassVersionError

Java: UnsupportedClassVersionError

Das Programm wurde mit einem neueren Java kompiliert, als es ausführt? Diese Meldung nennt sogar die Versionsnummern — man muss sie nur übersetzen.