NoClassDefFoundErrorSono tante le volte che mi capita di vedere persone in difficoltà quando devono compilare le loro applicazioni Java. Uno dei motivi che mi capita di affrontare più spesso è l’errore di cui discuteremo in questo articolo: NoClassDefFoundError.

Questo è un errore molto comune fra le persone che per la prima volta si avvicinano al mondo della programmazione Java ed è anche la causa di molte arrabbiature.

Cerchiamo di capire quando può comparire questo errore e perchè, fornendo come sempre anche la soluzione per ciascuna casistica.

NoClassDefFoundError (java.lang.NoClassDefFoundError, per la precisione), come dice la parola stessa, indica che la Java Virtual Machine (JVM) non è riuscita a trovare una classe, che il programma richiedeva (e che c’era quando è stato compilato! Almeno in teoria). Come prima analisi, dunque, osserviamo che è un errore che si sviluppa a tempo di esecuzione e non di compilazione. Questo significa che per il compilatore era tutto in ordine. Cerchiamo di capire, allora, perchè la JVM dia questo problema. Essenzialmente i casi in cui questo errore può saltar fuori sono i seguenti:

  1. Si cerca di avviare l’applicazione, omettendo il package
  2. E’ stata eliminata una classe (o un’intero package) dopo la compilazione
  3. Mancano delle librerie esterne di cui l’applicazione necessita
  4. La variabile d’ambiente CLASSPATH non è correttamente valorizzata
  5. Altre cause di natura più subdola

1. Si cerca di avviare l’applicazione, omettendo il package

Questo è l’errore più comune che viene commesso da chi si avvicina per la prima volta al linguaggio Java. Capita che, studiando il linguaggio su un libro di testo o seguendo alcuni esempi tratti da dispense trovate on-line, si faccia un bel copia & incolla di un programmino (magari il classico HelloWorld) e avviandolo compaia questo errore. Questo accade perchè nel 99% dei casi l’esempio che si va a copiare contiene la direttiva package. Chi ha scritto il programma non ha ritenuto necessario dare ulteriori informazioni (o forse dava per scontato che chi andava a provare l’applicazione le conoscesse già). Facciamo, allora, un esempio e vediamo di capire quando accade e come correggere:

package PrimoEsempio;
public class HelloWorld {
public static void main(String[] args) {
System.out.println(”Hello World!”);
}
}

Se non poniamo particolare attenzione a dove andiamo a collocare questo file otterremo di sicuro l’errore non appena andremo ad eseguire l’applicazione. La clausola package evidenziata nel codice, infatti, gioca un ruolo fondamentale nella collocazione dei file su disco. Nel caso del nostro codice, ad esempio, abbiamo specificato che la classe fa parte del package “PrimoEsempio” e questo ci obbliga a collocare il file all’interno di una directory che si chiama PrimoEsempio. Non solo: per poter avviare questa semplicissima applicazione, dovremo trovarci all’esterno di questa directory e seguire le regole di nomenclatura nella riga di comando. Se, ad esempio, abbiamo salvato il file HelloWorld.java all’interno della directory /home/progetti/PrimoEsempio/, allora per poter avviare l’applicazione dobbiamo trovarci nella directory /home/progetti/ e dare questo comando:

java PrimoEsempio.HelloWorld

Da notare che la compilazione può avvenire anche all’interno della directory PrimoEsempio, ma l’esecuzione no (ed in genere, si tende ad essere omogenei, compilando da fuori). Da notare il punto fra il nome del package e il nome della classe: la JVM utilizza questo carattere per separare i nomi dei package e automaticamente li converte nel separatore di directory per andarsi a cercare il file. Non è possibile utilizzare direttamente il separatore di directory.

2. E’ stata eliminata una classe (o un’intero package) dopo la compilazione

Questo tipo di problema è più raro del precedente, ma può capitare. Se stiamo lavorando con progetti di grosse dimensioni (con tante classi e/o tanti package) può capitare che, per fare un po’ di pulizia, cancelliamo delle classi ritenute “obsolete”. Generalmente, dopo questa operazione, è sempre consigliabile ricompilare l’intero progetto per non incorrere in questo tipo di problematica. Ma se ci dovesse sfuggire una compilazione, ecco che questo errore compare (magari non subito!). Il compilatore Java, infatti, quando deve risolvere un riferimento ad una classe, per risparmiare tempo utilizza questa sequenza di passi:

  1. Controlla se esiste il file class della classe in questione nella directory che sta esaminando e se esiste (ed è coerente) lo linka dove necessario;
  2. Se non esiste il file .class cerca il relativo file .java e se questo esiste lo compila e referenzia dove necessario;
  3. Se non esiste nemmeno il file .java, allora cerca il class all’interno del suo CLASSPATH e se lo trova (ed è coerente) lo linka dove necessario.

Questo sistema, ovviamente, non è perfetto! Proviamo ad osservare questa situazione:

A → B → C

Abbiamo la classe A, che fa uso della classe B, che a sua volta fa uso della classe C. Per compilare tutto è sufficiente chiedere di compilare la classe A: il compilatore noterà che A ha bisogno della classe B e quindi la compilerà, quindi noterà che B necessita della classe C e compilerà anche questa. Ora, dopo questo primo passaggio, per sbaglio andiamo a cancellare il file C.class. Quindi modifichiamo la classe A e la ricompiliamo. Cosa succede? Succede questo: il compilatore nota che A ha bisogno della classe B, trova il file B.class (quindi non deve ricompilare B.java) e lo linka… e qui sbaglia! Perchè B per funzionare ha bisogno della classe C, ma il file C.class è stato eliminato. Risultato: durante l’esecuzione ci troveremo ad incappare nel problema. La soluzione a questo problema è ovviamente quella di ricompilare il file C.class, ma se vogliamo prevenirla fin da subito, il consiglio che dò sempre io è quello di cancellare sempre tutti i .class della nostra applicazione, prima di effettuare la compilazione: questo garantisce la coerenza ed evita il problema.

3. Mancano delle librerie esterne di cui l’applicazione necessita

Questo caso è frequente quando qualcuno tenta di eseguire un’applicazione che ha scaricato da qualche sito. Non è raro, infatti, che chi mette a disposizione le proprie applicazioni, non includa con essa (per errore o per espressa volontà) le librerie che ha utilizzato. Ciò succede soprattutto quando le librerie sono a disposizione su Internet nel sito del produttore. Al primo avvio, l’applicazione va in errore e ci si chiede perchè. L’unico modo per ovviare a questo problema è quello di controllare sempre che l’applicazione che si va a scaricare (ed eseguire) sia accompagnata dalle librerie necessarie: generalmente queste sono sempre elencate nel sito di chi ha prodotto il prgoramma (o in un file di testo incluso con l’applicazione) e talvolta è necessario scaricarle separatamente (o andarsele a scaricare direttamente dal produttore).

Si può incappare in questo errore anche trasportando la nostra applicazione da un PC ad un altro: se sulla macchina che utilizziamo per sviluppare è installata la libreria, non è detto che lo stesso valga per la macchina su cui poi andremo ad eseguire; e la forza dell’abitudine spesso è causa di questo tipo di problemi.

4. La variabile d’ambiente CLASSPATH non è correttamente valorizzata

Questo caso è molto simile al precedente: la variabile d’ambiente CLASSPATH ricopre un ruolo importante tutte le volte che utilizziamo librerie esterne alla nostra applicazione. Come già accennato al punto 2, il compilatore va a cercare in questa variabile i percorsi in cui potrebbero trovarsi le classi che gli mancano… lo stesso farà la JVM quando dovrà eseguire. Se la variabile CLASSPATH è stata modificata oppure nel computer in cui portiamo l’applicazione essa non è correttamente configurata, la JVM non saprà dove trovare le classi di cui ha bisogno.

Il modo più semplice per evitare questo problema è non affidarsi ad essa: fare in modo che le librerie non siano esterne all’applicazione, ma sempre incluse nello stesso JAR (o, equivalente, nella stessa directory dell’applicazione), quindi andare ad eliminare dal sistema la variabile (che potrebbe portare ad altri problemi). In questo modo, le uniche directory che la JVM ispezionerà sono quella del core Java (dove risiedono le classi base) e quella dell’applicazione.

5. Altre cause di natura più subdola

Le cause che portano all’errore NoClassDefFoundError non sono certo solo quelle elencate finora. Ci possono essere altre cause, sempre riconducibili a quelle precedenti, che fanno scatenare questo errore. Un esempio potrebbe essere quello della rinomina dei file. Se sbadatamente andiamo a rinominare un file .class sarà molto probabile che l’applicazione che lo usa vada in errore visualizzando il messaggio NoClassDefFoundError. Altra causa è il caricamento dinamico di classi da parte di un’applicazione. Questa è una tecnica molto utilizzata da parte di chi scrive, ad esempio, applicazioni che fanno uso di Database: il driver JDBC del database viene, infatti, caricato dinamicamente attraverso una stringa che rappresenta il nome della classe da caricare. Se tale classe non esiste, si incappa nell’errore.

E l’insieme delle casistiche potrebbe continuare, ma una volta capito che tutti i casi sono riconducibili ai primi 4, non è difficile cercare la soluzione. E con questo concludo l’articolo, augurandovi come sempre una Buona Programmazione!

Tags: