java.lang.NoClassDefFoundError: Nie można zainicjować klasy XXX

165
public class PropHolder {
  public static Properties prop;

  static {
    //code for loading properties from file
  }
}

// Referencing the class somewhere else:
Properties prop = PropHolder.prop;

class PropHolderto moja klasa. Klasa znajduje się w tym samym pliku JAR klasy głównej. Tak więc nie powinno, ponieważ w ścieżce klas brakuje żadnego pliku JAR.

Kiedy przeglądam plik JAR według jar tf myjarfile, widzę PropHolder.classtam listę.

Btw: kod działa dobrze na moim komputerze lokalnym. Ale nie mogłem działać, gdy wdrożyłem go z jakimś skryptem na serwerze Linux. Więc myślę, że to nie jest problem z kodem. Ale z jakiegoś powodu. proces wdrażania jest bardzo trudny do śledzenia.

Jaki może być problem?

Leon
źródło
Czy struktura katalogów w Twoim pliku jar jest odpowiednia, aby pasowała do pakietu klas?
John B,
trzeba zobaczyć jakieś źródło, wiele rzeczy może to powodować. na przykład instrukcja „pakiet”, ale plik w rzeczywistości nie znajduje się w odpowiedniej ścieżce
jcomeau_ictx
3
Jedną z przyczyn jest wyjątek podczas inicjalizacji - czy jest jakiś inny wynik błędu?
Michael Brewer-Davis,

Odpowiedzi:

211

Moim najlepszym rozwiązaniem jest to, że jest tutaj problem:

static {
    //code for loading properties from file
}

Wygląda na to, że wystąpił nieprzechwycony wyjątek i został propagowany do właściwego ClassLoadera próbującego załadować klasę. Potrzebowalibyśmy jednak śledzenia stosu, aby to potwierdzić.

Albo to, albo wystąpiło podczas tworzenia PropHolder.propzmiennej statycznej.

John Vint
źródło
Wielokrotnie borykam się z tym samym problemem. Jestem pewien, że to z powodu staticproblemu. Co należy zrobić, aby rozwiązać problem?
viper
1
Będziesz musiał zidentyfikować, który wyjątek jest wyrzucany z staticbloku. Aby go debugować, umieść try/catch(Exception e)wokół całego bloku i zarejestruj wyjątek. Będziesz musiał naprawić ten wyjątek. Zazwyczaj Wyjątkiem będą rejestrowane, ale może być trudno znaleźć, ponieważ jest to istota zalogowany podczas classloading które mogą wydarzyć się bardzo wcześnie
John Vint
Tak, trzymałem kod w try catchbloku i powiedział Failed to initialize ClassA. Myślę, że to jest problem JVM. Uruchomiłem ponownie system i wszystko działało dobrze. Jak rozwiązać ten problem w przyszłości bez ponownego uruchamiania systemu i rozwiązać problem za pomocą prostego rozwiązania.
viper
Niepowodzenie zainicjowania ClassA jest efektem ubocznym z czegoś innego. Będziesz chciał przejrzeć, causejeśli jest dostępny. A NoClassDefFoundErrorjest zawsze powiązany z innym błędem, będziesz musiał poszukać go w dziennikach lub spróbować zalogować go bardziej poprawnie (np. Wymusić logowanie do nowego pliku w systemie plików)
John Vint
Byłoby łatwiejsze do śledzenia, gdyby Java zgłosiła błąd CouldNotInitializeStaticPartOfClassError lub coś takiego. Wtedy my jako deweloperzy wiedzielibyśmy, gdzie szukać.
Maarten
126

Otrzymujesz, java.lang.NoClassDefFoundErrorco NIE oznacza, że ​​brakuje twojej klasy (w takim przypadku otrzymasz java.lang.ClassNotFoundException). ClassLoader napotkał błąd podczas odczytywania definicji klasy podczas próby odczytania klasy.

Umieść try / catch wewnątrz swojego statycznego inicjatora i spójrz na wyjątek. Jeśli czytasz tam jakieś pliki i różni się to od lokalnego środowiska, jest to bardzo prawdopodobne, że przyczyną problemu (może nie można znaleźć pliku, brak uprawnień itp.).

jeha
źródło
1
Jednym z wyjaśnień jest to, że chociaż NoClassDefFoundError nie implikuje wyjątku ClassNotFoundException, nadal jest możliwą przyczyną NoClassDefFoundError.
John Vint,
1
buf gdybyś miał wyjątek ClassNotFoundException, to ClassLoader nigdy nie próbowałby załadować tej klasy, prawda?
jeha,
4
Klasa może ładować inną klasę, której nie znaleziono. Przyczyną w tym przypadku jest nadal wyjątek ClassNotFoundException
John Vint,
1
Powinienem był wyjaśnić, miałem na myśli wyjątek.
GetCause
1
Ten był bardzo pomocny, ponieważ spędziłem około 20 minut na sprawdzaniu, czy plik JAR (do którego należy zgłoszona klasa) jest dołączony. Kiedy zdałem sobie sprawę, że patrzę w złym kierunku, łatwo zrozumiałem, że prawdopodobnie brakuje jakiejś klasy adnotacji i voila, zgłoszona klasa została zadeklarowana jako @Stateless, więc po prostu dodałem odpowiednią zależność i mogłem przejść dalej. Dzięki za wskazówkę!
RAM237
33

NoClassDefFoundError nie daje zbyt wielu wskazówek, co poszło nie tak wewnątrz bloku statycznego. Dobrą praktyką jest zawsze mieć taki blok w statycznym {...} kodzie inicjującym:

static {
  try {

    ... your init code here

  } catch (Throwable t) {
    LOG.error("Failure during static initialization", t);
    throw t;
  }
}
Mark Hansen
źródło
Jak to zrobić w Kotlinie?
Marlon,
Dzięki za podpowiedź. W moim przypadku otrzymywałem NPE podczas statycznej inicjalizacji linii.
Abhishek
3

Miałem ten sam wyjątek, tak rozwiązałem problem:

Warunki wstępne:

  1. Klasa Junit (i test), która rozszerzyła kolejną klasę.

  2. ApplicationContext zainicjowany przy użyciu spring, który inicjuje projekt.

  3. Kontekst aplikacji został zainicjowany w metodzie @Before

Rozwiązanie:

Zainicjuj kontekst aplikacji z metody @BeforeClass, ponieważ klasa nadrzędna również wymagała niektórych klas, które zostały zainicjowane z kontekstu aplikacji.

Mam nadzieję, że to pomoże.

KerenSi
źródło
2

Jak wspomniano powyżej, może to być kilka przyczyn. W moim przypadku miałem statycznie zainicjowaną zmienną, która opierała się na brakującym wpisie w moim pliku właściwości. Dodano brakujący wpis do pliku właściwości i problem został rozwiązany.

TriMix
źródło
1

Zaledwie kilka dni temu spotkałem się z tym samym pytaniem, co twoje. Cały kod działa dobrze na mojej lokalnej maszynie, ale okazuje się, że wystąpił błąd (noclassdeffound & initialize). Dlatego publikuję swoje rozwiązanie, ale nie wiem dlaczego, po prostu przedstawiam możliwość. Mam nadzieję, że ktoś znajomy to wyjaśni. @ John Vint Najpierw pokażę Ci mój problem. Mój kod ma zarówno zmienną statyczną, jak i blok statyczny. Kiedy pierwszy raz spotkałem się z tym problemem, wypróbowałem rozwiązanie Johna Vinta i próbowałem złapać wyjątek. Jednak nic nie złapałem. Więc pomyślałem, że to dlatego, że zmienna statyczna (ale teraz wiem, że to to samo) i nadal nic nie znalazłem. Więc próbuję znaleźć różnicę między maszyną z systemem Linux a moim komputerem. Potem odkryłem, że ten problem występuje tylko wtedy, gdy kilka wątków działa w jednym procesie (nawiasem mówiąc, maszyna linux ma podwójne rdzenie i podwójne procesy). Oznacza to, że jeśli istnieją dwa zadania (oba używają kodu, który ma statyczny blok lub zmienne) uruchamiane w tym samym procesie, to idzie źle, ale jeśli działają w różnych procesach, oba są w porządku. W maszynie Linux używam

mvn -U clean  test -Dtest=path 

aby uruchomić zadanie, a ponieważ moja zmienna statyczna ma na celu uruchomienie kontenera (lub może zainicjujesz nowy program ładujący klasy), więc pozostanie on do zatrzymania jvm, a jvm zatrzyma się tylko wtedy, gdy zatrzymają się wszystkie zadania w jednym procesie. Każde zadanie uruchamia nowy kontener (lub classloader) i wprowadza zamieszanie w jvm. W rezultacie występuje błąd. Jak więc to rozwiązać? Moim rozwiązaniem jest dodanie nowego polecenia do polecenia maven i umieszczenie każdego zadania w tym samym kontenerze.

-Dxxx.version=xxxxx #sorry can't post more

Być może już rozwiązałeś ten problem, ale nadal masz nadzieję, że pomoże on innym, którzy napotykają ten sam problem.

Król Małp
źródło
Co więcej, gdy kod działa na komputerze z systemem Linux, postępuj zgodnie z powyższym błędem, pojawia się inny problem: java.lang.ExceptionInInitializerError: nulloznacza to, że nie mogę znaleźć klasy w classloader lub nie wiem, którą załadować (chyba). Czy spotkałeś to?
MonkeyKing
1

Miałem ten sam wyjątek - ale tylko podczas pracy w trybie debugowania, tak rozwiązałem problem (po 3 całych dniach): w pliku build.gradle miałem: "multiDexEnabled true" ustawione w sekcji defaultConfig.

        defaultConfig {
    applicationId "com.xxx.yyy"
    minSdkVersion 15
    targetSdkVersion 28
    versionCode 5123
    versionName "5123"
    // Enabling multidex support.
    multiDexEnabled true
}

ale najwyraźniej to nie wystarczyło. ale kiedy się zmieniłem:

public class MyAppClass  extends Application 

do:

public class MyAppClass  extends MultiDexApplication 

to go rozwiązało. mam nadzieję, że to komuś pomoże

Elad
źródło
0

Jeśli pracujesz nad projektem systemu Android, upewnij się, że nie wywołujesz żadnych metod statycznych na żadnych klasach systemu Android. Używam tylko JUnit + Mockito, więc może inne frameworki mogą pomóc ci całkowicie uniknąć problemu, nie jestem pewien.

Mój problem polegał na wywołaniu Uri.parse(uriString)w ramach statycznego inicjatora do testu jednostkowego. Klasa Uri to interfejs API systemu Android, dlatego kompilacja testów jednostkowych nie mogła jej znaleźć. Zmieniłem tę wartość na nullzamiast i wszystko wróciło do normy.

lifeson106
źródło
0

Miałem te same problemy :java.lang.NoClassDefFoundError: Nie można zainicjować klasy com.xxx.HttpUtils

static {
    //code for loading properties from file
}

jest to problem ze środowiskiem, co oznacza, że ​​właściwości w pliku application.yml są nieprawidłowe lub puste!

user8503957
źródło
0

Mam ten sam problem. Zainicjowałem obiekt fasoli w statycznym bloku, jak poniżej:

static {
    try{
        mqttConfiguration = SpringBootBeanUtils.<MqttConfiguration>getBean(MqttConfiguration.class);
    }catch (Throwable e){
        System.out.println(e);
    }
 }

Tylko dlatego, że proces mojej instalacji bean spowodował NPE, mam w tym kłopoty. Więc myślę, że powinieneś dokładnie sprawdzić swój statyczny blok kodu.

lai nan
źródło