HOWTO · Java

Java.Lang.NoClassDefFoundError behoben: Klassenfehler konnte nicht initialisiert werden

Dieses Tutorial demonstriert den Fehler java.lang.noclassdeffounderror: Klasse konnte nicht initialisiert werden, diskutiert mögliche Gründe und stellt die Lösung bereit.

Auf dieser Seite

Dieser Fehler bedeutet meist, dass eine Klasse zuvor während der statischen Initialisierung in derselben JVM fehlgeschlagen ist. Bei der ersten aktiven Verwendung wird häufig java.lang.ExceptionInInitializerError gemeldet; spätere Versuche, die Klasse in demselben ClassLoader zu verwenden, können java.lang.NoClassDefFoundError: Could not initialize class auslösen. Suchen Sie nach dem ersten Fehler und beheben Sie ihn. Die spätere Meldung ist oft eine Folge und nicht die ursprüngliche Ursache.

Das ist etwas anderes als java.lang.ClassNotFoundException: Diese Ausnahme bedeutet, dass der Code ausdrücklich versucht hat, eine Klasse anhand ihres Namens zu laden, sie aber nicht finden konnte. Auch eine fehlende Klassendefinition kann eine andere Form von NoClassDefFoundError auslösen, meist mit dem Hinweis, dass eine bestimmte Klasse nicht gefunden wurde. Die Formulierung „Could not initialize class“ verweist auf den Initialisierungszustand. Eine JAR-Datei hinzuzufügen oder den Classpath zu ändern, hilft nur, wenn der erste Fehler tatsächlich auf eine fehlende Abhängigkeit hinweist.

Bedeutung von NoClassDefFoundError: Could not initialize class

Wenn die JVM eine Klasse initialisiert, führt sie deren statische Feldinitialisierer und statische Initialisierungsblöcke aus. Schlägt dieser Vorgang fehl, markiert die JVM die Klasse für diesen ClassLoader als fehlerhaft. Bei einer späteren aktiven Verwendung in demselben ClassLoader kann der Initialisierer nicht einfach erneut ausgeführt werden; stattdessen kann ein NoClassDefFoundError auftreten. Ein Neustart des Prozesses ermöglicht einen neuen Initialisierungsversuch, behebt aber nicht den zugrunde liegenden Fehler.

Häufige Auslöser sind ein statischer Initialisierer, der eine Datei über einen vom Arbeitsverzeichnis abhängigen Pfad liest, fehlerhafte Konfiguration einliest, auf einen nicht verfügbaren Dienst zugreift oder eine fehlschlagende Abhängigkeit aufruft. Derselbe Code kann deshalb auf dem Rechner eines Entwicklers funktionieren und auf einem Server mit anderem Arbeitsverzeichnis, anderen Dateiberechtigungen, Umgebungsvariablen oder Abhängigkeiten scheitern. Das sind mögliche Ansatzpunkte für die Untersuchung, keine allgemeingültige Ursachenliste.

Vermeiden Sie empfindliche oder umgebungsabhängige Vorgänge während der statischen Initialisierung, wenn sie sich ausdrücklich beim Anwendungsstart ausführen lassen. Muss die Initialisierung fehlschlagen, erhalten Sie die Ursache und geben Sie genügend Kontext aus, um die betreffende Konfiguration oder Ressource zu erkennen.

Fehler mit einem statischen Initialisierer reproduzieren

Dieses kleine Programm löst zunächst einen Initialisierungsfehler aus und versucht danach, dieselbe Klasse erneut zu verwenden. Der zweite Versuch veranschaulicht, warum der spätere Fehler die hilfreiche ursprüngliche Ausnahme verdecken kann.

Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.

public class InitializationFailure {
  static class Settings {
    static {
      if (Boolean.parseBoolean("true")) {
        throw new IllegalStateException("configuration is missing");
      }
    }

    static void load() {
      System.out.println("settings loaded");
    }
  }

  public static void main(String[] args) {
    try {
      Settings.load();
    } catch (Throwable error) {
      System.out.println(error.getClass().getName());
      if (error.getCause() != null) {
        System.out.println(error.getCause());
      }
    }

    try {
      Settings.load();
    } catch (Throwable error) {
      System.out.println(error.getClass().getName());
    }
  }
}

Die Ausgabe lautet:

java.lang.ExceptionInInitializerError
java.lang.IllegalStateException: configuration is missing
java.lang.NoClassDefFoundError

Der erste Aufruf meldet den Fehler des Initialisierers und dessen Ursache. Beim zweiten Aufruf ist Settings in dieser JVM und diesem ClassLoader bereits als fehlerhaft markiert. Der genaue Wortlaut einer Fehlermeldung kann je nach Java-Implementierung variieren. Das Beispiel gibt deshalb die Namen der Ausnahmeklassen und die erste Ursache aus, statt sich auf eine VM-spezifische Meldung zu stützen.

In einer Anwendung könnte ein Eigenschaften-Container wie PropHolder eine Datei in einem statischen Feldinitialisierer laden. Fehlt diese Datei in einer Umgebung, ist sie dort nicht lesbar oder ist sie fehlerhaft formatiert, kann der erste Fehler die Klasse für den restlichen Prozess unbrauchbar machen. Ein relativer Pfad wird ausgehend vom Arbeitsverzeichnis des Prozesses aufgelöst. Dieses kann sich zwischen einer IDE, einem Dienstmanager, einem Container und einer Befehlsshell unterscheiden. Prüfen Sie dieses konkrete Muster als mögliche Ursache; der Stacktrace zeigt, ob es hier zutrifft.

Den ursprünglichen Initialisierungsfehler finden

Suchen Sie im betroffenen Prozess nach dem frühesten Protokolleintrag für die Klasse und nicht nur nach dem abschließenden NoClassDefFoundError. Durchsuchen Sie die vollständigen Protokolle nach ExceptionInInitializerError, dem Klassennamen und dem ersten Eintrag Caused by:. Die erste Ausnahme und ihre Stackframes zeigen meist, welcher Initialisierer fehlgeschlagen ist und welcher Vorgang den Fehler ausgelöst hat.

Prüfen Sie anhand dieser Hinweise die konkret betroffene Eingabe:

  • Bei einem Dateizugriff prüfen Sie in der fehlerhaften Umgebung den aufgelösten Pfad, das Arbeitsverzeichnis des Prozesses, das Vorhandensein und die Berechtigungen der Datei, ihre Zeichenkodierung und ihren Inhalt.
  • Bei einem Fehler beim Einlesen der Konfiguration prüfen Sie den konkreten Wert und geben Sie an, welche Einstellung nicht verarbeitet werden konnte.
  • Bei einem Aufruf einer Abhängigkeit untersuchen Sie deren eigene Fehlerursache und Laufzeitversion sowie die Frage, ob der benötigte Dienst beim Start verfügbar ist.
  • Bei Problemen mit der Initialisierungsreihenfolge oder Zyklen untersuchen Sie statische Felder, die aufeinander zugreifen, und vermeiden Sie Abhängigkeiten von teilweise initialisiertem Zustand.

Leiten Sie allein aus dem Wort „class“ kein Classpath-Problem ab. Nennt die erste Ursache eine fehlende Klasse oder Methode, prüfen Sie die Laufzeitabhängigkeiten und deren Versionskompatibilität. Beheben Sie andernfalls den Vorgang, den die erste Ursache nennt. Enthalten die Protokolle nur den späteren Fehler, reproduzieren Sie das Problem mit Startprotokollierung oder einem gezielten Test, der eine neue JVM startet. Eine fehlgeschlagene Klasse lässt sich innerhalb ihres bestehenden ClassLoaders nicht zuverlässig zurücksetzen.

Den Initialisierer korrigieren und die Anwendung überprüfen

Beheben Sie die fehlgeschlagene Eingabe oder den fehlgeschlagenen Vorgang. Stellen Sie beispielsweise die Eigenschaftendatei an einem stabil konfigurierten Ort bereit, verwenden Sie einen expliziten Pfad statt eines zufälligen Arbeitsverzeichnisses, validieren Sie erforderliche Werte beim Start und geben Sie eine aussagekräftige Fehlermeldung mit dem Namen der Ressource oder Einstellung aus. Hängt der Wert von der Laufzeitkonfiguration ab, verwenden Sie vorzugsweise eine ausdrückliche Initialisierungsmethode, die dem Aufrufer einen hilfreichen Fehler zurückgeben kann.

Fangen Sie eine Initialisierungsausnahme nicht ab, um anschließend mit einem Nullwert oder einem unvollständigen Wert fortzufahren. Dadurch wird die ursprüngliche Ursache verdeckt, während die Anwendung in einem unsicheren Zustand bleibt. Ist ein Ersatzwert für die Anwendung geeignet, legen Sie ihn bewusst fest und validieren Sie ihn. Andernfalls brechen Sie den Start ab und fügen Sie der Fehlermeldung die ursprüngliche Ursache hinzu.

Starten Sie nach der Behebung des Fehlers einen neuen Prozess und überprüfen Sie, ob sowohl die betroffene Klasse erfolgreich initialisiert wird als auch der Anwendungsablauf funktioniert, der sie verwendet. Eine einfache erfolgreiche statische Initialisierung sieht so aus:

Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.

public class InitializationCheck {
  static class Settings {
    static final String MODE = "ready";
  }

  public static void main(String[] args) {
    System.out.println(Settings.MODE);
  }
}

Die Ausgabe lautet:

ready

Wiederholen Sie die Prüfung anschließend in der Umgebung, in der das Problem aufgetreten ist, und verwenden Sie dabei dasselbe Dienstkonto, Arbeitsverzeichnis, dieselbe Konfiguration und dieselben Laufzeitabhängigkeiten. Ein Neustart kann das Symptom vorübergehend verschwinden lassen. Verlässlich ist die Überprüfung erst, wenn der ursprünglich fehlschlagende Vorgang mit der vorgesehenen Konfiguration erfolgreich ausgeführt wird.