¿Qué significa "No se pudo encontrar o cargar la clase principal"?

Un problema común que experimentan los nuevos desarrolladores de Java es que sus programas no se ejecutan con el mensaje de error No se pudo encontrar o cargar la clase principal ...

¿Qué significa esto, qué lo causa y cómo debe solucionarse?

Solución

La sintaxis del comando java.

En primer lugar, hay que entender la forma correcta de lanzar un programa utilizando el comando java (o javaw). La sintaxis normal1 es esta:

    java [  ... ]  [ ...]

donde es una opción de la línea de comandos (que comienza con un carácter "-"), es un nombre de clase Java completamente calificado, y `` es un argumento arbitrario de la línea de comandos que se pasa a su aplicación.
1 - Hay una segunda sintaxis para los archivos JAR "ejecutables" que describiré al final. El nombre completamente calificado (FQN) para la clase se escribe convencionalmente como lo haría en el código fuente de Java; por ejemplo

    packagename.packagename2.packagename3.ClassName

Sin embargo, algunas versiones del comando java permiten utilizar barras en lugar de puntos; por ejemplo

    packagename/packagename2/packagename3/ClassName

que (confusamente) parece un nombre de ruta de archivo, pero no lo es. Tenga en cuenta que el término nombre completamente cualificado es terminología estándar de Java ... no algo que me haya inventado para confundirle :-) Este es un ejemplo de cómo debería ser un comando java:

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

Lo anterior hará que el comando java haga lo siguiente

  1. Buscar la versión compilada de la clase com.acme.example.ListUsers.
  2. Cargar la clase.
  3. Comprueba que la clase tiene un método main con firma, tipo de retorno y modificadores dados por public static void main(String[]). (Nota, el nombre del argumento del método NO forma parte de la firma).
  4. Llame a ese método pasándole los argumentos de la línea de comandos ("fred", "joe", "bert") como String[]. Razones por las que Java no puede encontrar la clase

    Cuando se obtiene el mensaje "No se pudo encontrar o cargar la clase principal...", significa que el primer paso ha fallado. El comando java no ha podido encontrar la clase. Y de hecho, el "..." en el mensaje será el nombre de la clase completamente calificado que java está buscando. Entonces, ¿por qué no puede encontrar la clase?
    Razón #1 - has cometido un error con el argumento classname

    La primera causa probable es que hayas proporcionado un nombre de clase incorrecto. (O ... el nombre de clase correcto, pero en la forma incorrecta.) Considerando el ejemplo anterior, aquí hay una variedad de formas incorrectas de especificar el nombre de la clase:

  • Ejemplo #1 - un nombre de clase simple:

    java ListUser

    Cuando la clase está declarada en un paquete como com.acme.example, entonces debes usar el nombre completo de la clase incluyendo el nombre del paquete en el comando java; por ejemplo java com.acme.example.ListUser

  • Ejemplo #2 - un nombre de archivo o ruta en lugar de un nombre de clase: java ListUser.class java com/acme/example/ListUser.class

  • Ejemplo #3 - un nombre de clase con el casing incorrecto: java com.acme.example.listuser

  • Ejemplo #4 - un error tipográfico java com.acme.example.mistuser

  • Ejemplo #5 - un nombre de archivo fuente java ListUser.java

  • Ejemplo #6 - se olvidó el nombre de la clase por completo java muchos argumentos Razón #2 - el classpath de la aplicación está mal especificado

    La segunda causa probable es que el nombre de la clase sea correcto, pero que el comando java no pueda encontrar la clase. Para entender esto, es necesario entender el concepto de "classpath". Esto se explica bien en la documentación de Oracle:

  • La documentación del comando java

  • Configuración del classpath.

  • El tutorial de Java - PATH y CLASSPATH Así que ... si ha especificado el nombre de la clase correctamente, lo siguiente que hay que comprobar es que ha especificado el classpath correctamente:

  1. Lee los tres documentos enlazados arriba. (Sí... Léalos. Es importante que un programador de Java entienda al menos lo básico de cómo funcionan los mecanismos de classpath de Java).
  2. Mira la línea de comandos y / o la variable de entorno CLASSPATH que está en efecto cuando ejecutas el comando java. 2. Comprueba que los nombres de los directorios y de los archivos JAR son correctos.
  3. Si hay nombres de ruta relativos en el classpath, comprueba que se resuelven correctamente ... desde el directorio actual que está en vigor cuando ejecutas el comando java.
  4. Compruebe que la clase (mencionada en el mensaje de error) puede ser localizada en el classpath efectivo.
  5. Tenga en cuenta que la sintaxis del classpath es diferente para Windows frente a Linux y Mac OS. (El separador del classpath es ; en Windows y : en los demás. Si utiliza el separador incorrecto para su plataforma, no obtendrá un mensaje de error explícito. En su lugar, obtendrá un archivo o directorio inexistente en la ruta que será ignorado silenciosamente). Razón #2a - el directorio equivocado está en el classpath

    Cuando se pone un directorio en el classpath, éste corresponde teóricamente a la raíz del espacio de nombres cualificados. Las clases se ubican en la estructura de directorios debajo de esa raíz, mapeando el nombre completamente calificado a un nombre de ruta. Así, por ejemplo, si "/usr/local/acme/classes" está en la ruta de clases, entonces cuando la JVM busca una clase llamada com.acme.example.Foon, buscará un archivo ".class" con este nombre de ruta:

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

Si hubieras puesto "/usr/local/acme/classes/com/acme/example" en el classpath, entonces la JVM no podría encontrar la clase. Razón #2b - la ruta del subdirectorio no coincide con el FQN

Si el FQN de sus clases es com.acme.example.Foon, entonces la JVM va a buscar "Foon.class" en el directorio "com/acme/example":

  • Si su estructura de directorios no coincide con el nombre del paquete según el patrón anterior, la JVM no encontrará su clase.
  • Si intentas renombrar una clase moviéndola, eso también fallará ... pero el stacktrace de la excepción será diferente. Es probable que diga algo como esto Causado por: java.lang.NoClassDefFoundError: (nombre incorrecto: ) porque el FQN del fichero de clases no coincide con lo que el cargador de clases espera encontrar. Para dar un ejemplo concreto, suponiendo que
  • quieres ejecutar la clase com.acme.example.Foon,
  • la ruta completa del fichero es /usr/local/acme/classes/com/acme/example/Foon.class,
  • su directorio de trabajo actual es /usr/local/acme/classes/com/acme/example/, entonces:
# 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

Notas:

  • La opción -classpath puede acortarse a -cp en la mayoría de las versiones de Java. Compruebe las respectivas entradas del manual para java, javac, etc.

  • Piense cuidadosamente cuando elija entre nombres de ruta absolutos y relativos en los classpaths. Recuerde que un nombre de ruta relativo puede "romperse" si el directorio actual cambia.
    Razón #2c - dependencias que faltan en el classpath

    El classpath necesita incluir todas las otras clases (no del sistema) de las que depende su aplicación. (Las clases del sistema se localizan automáticamente, y rara vez necesitas preocuparte por esto). Para que la clase principal se cargue correctamente, la JVM necesita encontrar:

  • la propia clase.

  • todas las clases e interfaces en la jerarquía de superclases (por ejemplo, ver https://stackoverflow.com/questions/42880748)

  • todas las clases e interfaces a las que se hace referencia mediante declaraciones de variables o llamadas a métodos o expresiones de acceso a campos. (Nota: las especificaciones de JLS y JVM permiten cierto margen para que una JVM cargue las clases "perezosamente", y esto puede afectar a cuándo se lanza una excepción del cargador de clases). Razón #3 - la clase ha sido declarada en el paquete equivocado

    Ocasionalmente ocurre que alguien pone un archivo de código fuente en la la carpeta equivocada en su árbol de código fuente, o que omiten la declaración del paquete. Si haces esto en un IDE, el compilador del IDE te lo dirá inmediatamente. Del mismo modo, si utiliza una herramienta de compilación de Java decente, la herramienta ejecutará javac de manera que detectará el problema. Sin embargo, si construyes tu código Java a mano, puedes hacerlo de tal manera que el compilador no note el problema, y el archivo ".class" resultante no esté en el lugar que esperas. ¿Sigues sin encontrar el problema?

    Hay muchas cosas que comprobar, y es fácil pasar por alto algo. Intenta añadir la opción -Xdiag a la línea de comandos java (como la primera cosa después de java). Esta opción mostrará varias cosas sobre la carga de clases, y esto puede ofrecerte pistas sobre cuál es el verdadero problema. Además, considere los posibles problemas causados por copiar y pegar caracteres invisibles o no ASCII de sitios web, documentos, etc. Y ten en cuenta los "homoglifos", cuando dos letras o símbolos parecen iguales... pero no lo son.

    La sintaxis java -jar.

    La sintaxis alternativa utilizada para los archivos JAR "ejecutables" es la siguiente:

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

por ejemplo

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

En este caso, el nombre de la clase de entrada (es decir, com.acme.example.ListUser) y el classpath se especifican en el MANIFIESTO del archivo JAR.

IDEs

Un IDE Java típico tiene soporte para ejecutar aplicaciones Java en la propia JVM del IDE o en una JVM hija. Estas son generalmente inmunes a esta excepción particular, porque el IDE utiliza sus propios mecanismos para construir el classpath en tiempo de ejecución, identificar la clase principal y crear la línea de comandos java. Sin embargo, todavía es posible que esta excepción ocurra, si usted hace cosas a espaldas del IDE. Por ejemplo, si has configurado previamente un lanzador de aplicaciones para tu aplicación Java en Eclipse, y luego has movido el archivo JAR que contiene la clase "main" a un lugar diferente en el sistema de archivos sin decírselo a Eclipse, Eclipse lanzaría involuntariamente la JVM con un classpath incorrecto. En resumen, si tienes este problema en un IDE, comprueba cosas como el estado del IDE, referencias de proyecto rotas o configuraciones del lanzador rotas. También es posible que un IDE simplemente se confunda. Los IDEs son piezas de software enormemente complicadas que comprenden muchas partes que interactúan. Muchas de estas partes adoptan varias estrategias de almacenamiento en caché con el fin de hacer que el IDE en su conjunto responda. A veces pueden fallar, y un posible síntoma son los problemas al lanzar las aplicaciones. Si sospechas que esto puede estar ocurriendo, vale la pena probar cosas como reiniciar tu IDE y reconstruir el proyecto.

Otras referencias

Comentarios (11)

A veces lo que puede estar causando el problema no tiene nada que ver con la clase principal, y tuve que descubrir esto de la manera más difícil. Era una biblioteca referenciada que moví, y me dio el:

No se pudo encontrar o cargar la clase principal xxx Linux

Simplemente borré esa referencia, la añadí de nuevo, y volvió a funcionar bien.

Comentarios (5)

En primer lugar, establezca la ruta de acceso mediante este comando;

set path="paste the set path address"

A continuación, debe cargar el programa. Escriba "cd (nombre de la carpeta)" en la unidad almacenada y compilarlo. Por ejemplo, si mi programa almacenado en la unidad D, escriba "D:" pulse enter y escriba " cd (nombre de la carpeta)".

Comentarios (3)