Sono 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:
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.
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:
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.
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.
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.
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:classpath compilazione errore java NoClassDefFoundError package
5 Risposte
7 giorni di links - The best of My/Del.icio.us #5 » Traffyk - I’m driving crazy, Blog
November 25th, 2007 at 16:52
1[…] Il famigerato NoClassDefFoundError | BradipoMissile, riguarda java […]
BradipoMissile
November 26th, 2007 at 02:41
2@ Traffyk
Grazie come sempre per la pubblicità AGGRATIS del mio progetto!!!
Ciao!
D0n Vit0 CF
November 28th, 2007 at 12:19
3Grazie per l’aiuto! Ho risolto riflettendo sulla quinta causa e devo dire che in fondo era anche una cosa facile:
il compilatore generava due file .class per la stessa classe, uno con nome.class e uno con nome$1.class (questa cosa devo ancora capirla…); io includevo solo uno dei due (nome.class, ovviamente) e da qui l’errore… GRAZIE X AVERMI AIUTATO A RIFLETTERE!
————————————————————————–
CF stands for carry flag!
BradipoMissile
November 28th, 2007 at 15:00
4@ D0n Vit0 CF
Ciao e benvenuto nel Bradipo Blog!
Pur non capendone una mazza di Java mi rendo conto che tu ne sai un Tot più di me e potresti essere utile alla causa del nostro progetto!
Che ne diresti di dare una mano a LeleFT, magari scrivendo qualche articoletto tecnico qui nel blog, nella sezione Programmazione Java?!?
Se ti può interessare, scrivi a Lele e mettiti d’accordo con lui!
La sua mail è: leleft@bradipomissile.net
Ciaoooo!!!
LeleFT
November 28th, 2007 at 15:16
5@D0n Vit0 CF
Quei file che terminano con $1, $2, ecc sono i compilati delle classi anonime di cui il tuo programma fa uso. E’ pratica comune, ad esempio, associare un ActionListener anonimo ad un pulsante, senza creare un oggetto ActionListener, ma creando l’oggetto con l’istruzione new direttamente nel metodo addActionListener del pulsante.
Anche quelle classi, essendo necessarie, possono sollevare l’eccezione.
RSS Iscriviti ai Feed dei commenti di questo articolo · TrackBack URI
Lascia un commento... please!!!
Autori
Categories
Calendario
Tag Cloud
Sondaggio
Blog Amici
Gente "Avanti"
Siti Interessanti
Benvenuto!
Pensieri / Aggiornamenti
Qualche statistica
Blog segnalato su
Recent Posts
Recent Comments
Meta