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.