Cosa significa "Impossibile trovare o caricare la classe principale"?

Un problema comune che i nuovi sviluppatori Java sperimentano è che i loro programmi non riescono a funzionare con il messaggio di errore: "Impossibile trovare o caricare la classe principale...`

Cosa significa questo, cosa lo causa e come si dovrebbe risolvere?

Soluzione

La sintassi del comando java

Prima di tutto, devi capire il modo corretto di lanciare un programma usando il comando java (o javaw). La sintassi normale1 è questa:

    java [  ... ]  [ ...]

dove è un'opzione della linea di comando (che inizia con un carattere "-"), è un nome di classe Java pienamente qualificato, e `` è un argomento arbitrario della linea di comando che viene passato alla tua applicazione.
1 - C'è una seconda sintassi per i file JAR "eseguibili" che descriverò in fondo; Il nome pienamente qualificato (FQN) per la classe è convenzionalmente scritto come si farebbe nel codice sorgente Java; ad esempio

    packagename.packagename2.packagename3.ClassName

Tuttavia alcune versioni del comando java permettono di usare slash al posto dei punti; ad es.

    packagename/packagename2/packagename3/ClassName

che (confusamente) sembra un percorso di file, ma non lo è. Notate che il termine nome completamente qualificato è terminologia Java standard ... non qualcosa che ho inventato per confondervi :-) Ecco un esempio di come dovrebbe essere un comando java:

    java -Xmx100m com.acme.example.ListUsers fred joe bert

Quanto sopra farà sì che il comando java faccia quanto segue:

  1. Cerca la versione compilata della classe com.acme.example.ListUsers.
    1. Carica la classe.
  2. Controllate che la classe abbia un metodo main con signature, return type e modificatori dati da public static void main(String[]). (Nota, il nome dell'argomento del metodo è NON parte della firma).
  3. Chiama quel metodo passandogli gli argomenti della linea di comando ("fred", "joe", "bert") come String[]. Motivi per cui Java non riesce a trovare la classe

    Quando ottieni il messaggio "Could not find or load main class ...", significa che il primo passo è fallito. Il comando java non è riuscito a trovare la classe. E infatti, il "..." nel messaggio sarà il nome della classe qualificata che java sta cercando. Quindi perché potrebbe non essere in grado di trovare la classe?
    Motivo #1 - hai fatto un errore con l'argomento classname

    La prima probabile causa è che tu possa aver fornito il nome della classe sbagliato. (O ... il nome giusto della classe, ma nella forma sbagliata.) Considerando l'esempio precedente, ecco una varietà di modi sbagliati per specificare il nome della classe:

  • Esempio #1 - un semplice nome di classe:

    java ListUser

    Quando la classe è dichiarata in un pacchetto come com.acme.example, allora devi usare il nome completo della classe incluso il nome del pacchetto nel comando java; ad esempio java com.acme.example.ListUser

  • Esempio #2 - un nome di file o un percorso piuttosto che un nome di classe: java ListUser.class java com/acme/example/ListUser.class

  • Esempio #3 - un nome di classe con il casing errato: java com.acme.example.listuser

  • Esempio #4 - un errore di battitura java com.acme.example.mistuser

  • Esempio #5 - un nome di file sorgente java ListUser.java

  • Esempio #6 - hai dimenticato completamente il nome della classe java molti argomenti Motivo #2 - il classpath dell'applicazione non è specificato correttamente

    La seconda probabile causa è che il nome della classe sia corretto, ma che il comando java non riesca a trovare la classe. Per capire questo, è necessario comprendere il concetto di "classpath". Questo è spiegato bene dalla documentazione Oracle:

  • La documentazione del comando java

  • Impostazione del classpath.

  • Il tutorial Java - PATH e CLASSPATH Quindi ... se avete specificato correttamente il nome della classe, la prossima cosa da controllare è che abbiate specificato correttamente il classpath:

  1. Leggete i tre documenti collegati sopra. (Sì ... LEGGILI. È importante che un programmatore Java comprenda almeno le basi di come funziona il meccanismo del classpath Java).
  2. Guardate la linea di comando e / o la variabile d'ambiente CLASSPATH che è in vigore quando si esegue il comando java. Controllate che i nomi delle directory e dei file JAR siano corretti.
  3. Se ci sono nomi di percorso relativi nel classpath, controllare che si risolvano correttamente ... dalla directory corrente che è in vigore quando si esegue il comando java.
  4. Controllare che la classe (menzionata nel messaggio di errore) possa essere localizzata nel classpath effettivo.
  5. Nota che la sintassi del classpath è diversa per Windows rispetto a Linux e Mac OS. (Il separatore di classpath è ; su Windows e : sugli altri. Se usi il separatore sbagliato per la tua piattaforma, non riceverai un messaggio di errore esplicito. Invece, otterrete un file o una directory inesistente sul percorso che verrà silenziosamente ignorato). Motivo #2a - la directory sbagliata è nel classpath

    Quando mettete una directory nel classpath, essa corrisponde teoricamente alla radice dello spazio dei nomi qualificati. Le classi si trovano nella struttura di directory sotto quella radice, mappando il nome pienamente qualificato ad un percorso. Così, per esempio, se "/usr/local/acme/classes" è nel percorso della classe, allora quando la JVM cerca una classe chiamata com.acme.example.Foon, cercherà un file ".class" con questo pathname:

  /usr/local/acme/classes/com/acme/example/Foon.class

Se tu avessi messo "/usr/local/acme/classes/com/acme/example" nel classpath, allora la JVM non sarebbe in grado di trovare la classe. Motivo #2b - il percorso della sottodirectory non corrisponde al FQN

Se la tua classe FQN è com.acme.example.Foon, allora la JVM cercherà "Foon.class" nella directory "com/acme/example":

  • Se la struttura della vostra directory non corrisponde alla denominazione del pacchetto secondo lo schema di cui sopra, la JVM non troverà la vostra classe.
  • Se tentate di rinominare una classe spostandola, anche questo fallirà ... ma lo stacktrace delle eccezioni sarà diverso. Potrebbe dire qualcosa del genere: Caused by: java.lang.NoClassDefFoundError: <path> (nome sbagliato: ) perché il FQN nel file di classe non corrisponde a quello che il caricatore di classi si aspetta di trovare. Per fare un esempio concreto, supponiamo che:
  • si voglia eseguire la classe com.acme.example.Foon,
  • il percorso completo del file è /usr/local/acme/classes/com/acme/example/Foon.class,
  • la tua directory di lavoro corrente è /usr/local/acme/classes/com/acme/example/, allora:
# wrong, FQN is needed
java Foon

# wrong, there is no `com/acme/example` folder in the current working directory
java com.acme.example.Foon

# wrong, similar to above
java -classpath . com.acme.example.Foon

# fine; relative classpath set
java -classpath ../../.. com.acme.example.Foon

# fine; absolute classpath set
java -classpath /usr/local/acme/classes com.acme.example.Foon

Note:

  • L'opzione -classpath può essere abbreviata in -cp nella maggior parte delle versioni di Java. Controlla le rispettive voci di manuale per java, javac e così via.

  • Pensaci bene quando scegli tra nomi di percorso assoluti e relativi nei classpath. Ricordate che un pathname relativo può "rompere" se la directory corrente cambia.
    Motivo #2c - dipendenze mancanti dal classpath

    Il classpath deve includere tutte le altre classi (non di sistema) da cui dipende la tua applicazione. (Le classi di sistema sono localizzate automaticamente, e raramente hai bisogno di preoccupartene). Per caricare correttamente la classe principale, la JVM deve trovare:

  • la classe stessa.

  • tutte le classi e le interfacce nella gerarchia delle superclassi (per esempio, vedi https://stackoverflow.com/questions/42880748)

  • tutte le classi e le interfacce a cui si fa riferimento per mezzo di dichiarazioni di variabili o variabili, o espressioni di chiamata di metodo o di accesso al campo. (Nota: le specifiche JLS e JVM permettono ad una JVM di caricare classi "pigramente", e questo può influenzare quando viene lanciata un'eccezione classloader). Motivo #3 - la classe è stata dichiarata nel pacchetto sbagliato

    Succede occasionalmente che qualcuno metta un file di codice sorgente nella nella cartella sbagliata nel loro albero del codice sorgente, o tralascia la dichiarazione package. Se lo fate in un IDE, il compilatore dell'IDE ve lo dirà immediatamente. Allo stesso modo, se si utilizza uno strumento di compilazione Java decente, lo strumento eseguirà javac in modo da rilevare il problema. Tuttavia, se costruisci il tuo codice Java a mano, puoi farlo in modo tale che il compilatore non noti il problema, e il file ".class" risultante non è nel posto che ti aspetti che sia. Non riesci ancora a trovare il problema?

    Ci sono molte cose da controllare, ed è facile perdere qualcosa. Prova ad aggiungere l'opzione -Xdiag alla linea di comando java (come prima cosa dopo java). Mostrerà varie cose sul caricamento delle classi, e questo potrebbe offrirti indizi su quale sia il vero problema. Inoltre, considera possibili problemi causati dal copiare e incollare caratteri invisibili o non-ASCII da siti web, documenti e così via. E considerate gli "omografi", dove due lettere o simboli sembrano uguali... ma non lo sono.

    La sintassi java -jar

    La sintassi alternativa usata per i file JAR "eseguibili" è la seguente:

  java [  ... ] -jar  [ ...]

ad es.

  java -Xmx100m -jar /usr/local/acme-example/listuser.jar fred

In questo caso il nome della classe entry-point (cioè com.acme.example.ListUser) e il classpath sono specificati nel MANIFEST del file JAR.

IDE

Un tipico IDE Java ha il supporto per eseguire applicazioni Java nella stessa JVM dell'IDE o in una JVM figlia. Questi sono generalmente immuni da questa particolare eccezione, perché l'IDE usa i propri meccanismi per costruire il classpath di runtime, identificare la classe principale e creare la linea di comando java. Tuttavia è ancora possibile che questa eccezione si verifichi, se si fanno cose alle spalle dell'IDE. Per esempio, se avete precedentemente impostato un Application Launcher per la vostra applicazione Java in Eclipse, e poi avete spostato il file JAR che contiene la classe "main" in un posto diverso nel file system senza dirlo a Eclipse, Eclipse potrebbe involontariamente lanciare la JVM con un classpath errato. In breve, se avete questo problema in un IDE, controllate cose come lo stato stantio dell'IDE, riferimenti al progetto non funzionanti o configurazioni del lanciatore non corrette. È anche possibile che un IDE sia semplicemente confuso. Gli IDE sono pezzi di software enormemente complicati che comprendono molte parti che interagiscono. Molte di queste parti adottano varie strategie di caching per rendere l'IDE nel suo complesso reattivo. Queste possono a volte andare male, e un possibile sintomo sono i problemi quando si lanciano le applicazioni. Se sospettate che questo possa accadere, vale la pena provare cose come riavviare l'IDE e ricostruire il progetto.

Altri riferimenti

Commentari (11)

A volte ciò che potrebbe causare il problema non ha nulla a che fare con la classe principale, e ho dovuto scoprirlo nel modo più difficile. Era una libreria di riferimento che ho spostato, e mi ha dato il:

Impossibile trovare o caricare la classe principale xxx Linux

Ho semplicemente cancellato quel riferimento, l'ho aggiunto di nuovo e ha funzionato di nuovo bene.

Commentari (5)

Prima imposta il percorso usando questo comando;

set path="paste the set path address"

Poi devi caricare il programma. Digita "cd (nome della cartella)" nell'unità memorizzata e compilalo. Per esempio, se il mio programma è memorizzato nell'unità D, digitate "D:" premete invio e digitate " cd (nome cartella)".

Commentari (3)