Solucionar java.lang.NoClassDefFoundError en Java (Y Qué Es)



El primer error frecuente es no saber diferenciar java.lang.ClassNotfoundException y NoClassDefFoundError. En realidad son totalmente diferentes. El segundo error es intentar resolver java.lang.NoClassDefFoundError con prueba y error, en vez de entender porqué aparece este error.
 

¿Porqué Aparece NoClassDefFoundError en Java?


NoClassDefFoundError aparece cuando la máquina virtual de Java no puede encontrar una clase particular en tiempo de ejecución, que si estaba disponible en tiempo de compilación. Por ejemplo, si tenemos una llamada a un método desde una clase o accedemos a cualquier miembro estático de una clase, y esa clase no está disponible en tiempo de ejecución, entonces la JVM lanzará un NoClassDefFoundError. Es importante entender la diferencia con ClassNotFoundException, que aparece cuando queremos cargar una clase en tiempo de ejecución solamente y no fue proporcionado el nombre en tiempo de compilación. Muchos desarrolladores Java mezclan estos 2 errores y se confunden.

En breve, NoClassDefFoundError aparece cuando una clase esta presente en tiempo de compilación, pero no esta disponible en el classpath de Java en tiempo de ejecución.


El mensaje que aparece cuando hay un NoClassDefFoundError es:

 Exception in thread "main" java.lang.NoClassDefFoundError

Exception in thread "main" simplemente indica que es el hilo principal el que no ha podido encontrar una clase particular. Puede ser cualquier otro hilo. La diferencia entre que el error provenga del hilo principal o provenga de otro hilo, es que si ocurre en el hilo principal el programa se termina inmediatamente, lo que no ocurre si el error aparece en otro hilo.


Como Resolver java.lang.NoClassDefFoundError

 
Como ya comentamos, la razón de NoClassDefFoundError es que una clase particular no esta disponible en el classpath, asi que tenemos que agregarlo al classpath o tenemos que verificar porqué no esta disponible. Pueden haber múltiples razones:

  1. La clase no está disponible en el classpath de Java
  2. Puedes estar ejecutando un programa usando el comando jar y la clase no está definida en el atributo classpath del archivo manifest.
  3. Algún script de inicio puede estar sobre-escribiendo la variable de entorno de classpath.
  4. Como  java.lang.NoClassDefFoundError es una sub-clase de java.lang.LinkageError, puede ser que una de tus dependencias, como librerias nativas, no esten disponibles.
  5. Verifica si tienes algún  java.lang.ExceptionInInitializerError en tu log. Tener NoClassDefFoundError debido a un fallo en una inicialización estática es muy común.
  6. Si trabajas en un entorno J2EE, la visibilidad de una clase entre diferentes cargadores de clase o classloaders pueden también causar NoClassDefFoundError. Ver los ejemplos y escenarios mas abajo.


NoClassDefFoundError en Java: Ejemplos y Escenarios

  1. Una clase pertenece a un jar faltante o el jar no fue agregado al classpath, o a veces el nombre del jar puede haber sido cambiado.
  2. La clase no está en el classpath. Puedes mirar al resultado de System.getproperty("java.classpath") para ver el classpath, desde el cuál podrás tener al menos una idea de tu classpath de tiempo de ejecución real.
  3. Intenta ejecutar la opción -classpath con el classpath que crees que va a funcionar, y si funciona significa que algo esta sobre-escribiendo el classpath de java.
  4.  Una excepción en un bloque estático de inicialización es otra razón común para tener un NoClassDefFoundError. Esto ocurre cuando tu clase realiza una inicialización estática en un bloque estático, como en muchas clases singleton que se inicializan a si mismos o toman ventaja de la seguridad de hilos proporcionada por la JVM durante el proceso de inicialización de clases, y si el bloque estático lanza una excepción, la clase que referencia esta clase tendrá un NoClassDefFoundError. Si miras en tu log, debes buscar por cualquier  java.lang.ExceptionInInitializerError porque esa podría ser lo que lance un java.lang.NoClassDefFoundError: Could not initialize class en otros lugares.

**
 * Programa que demuestra un fallo de inicialización estática que luego causa una
 * java.lang.NoClassDefFoundError en Java.
 * @author Jairo Honorio
 */
public class NoClassDefFoundErrorPorInicializacionEstatica {

    public static void main(String args[]){
       
        List<Usuario> usuarios = new ArrayList<Usuario>(2);
       
        for(int i=0; i<2; i++){
            try{
            usuarios.add(new Usuario(String.valueOf(i))); //lanzará NoClassDefFoundError
            }catch(Throwable t){
                t.printStackTrace();
            }
        }        
    }
}

class Usuario {
    private static String USUARIO_ID = getUsuarioId();
   
    public Usuario(String id){
        this.USUARIO_ID = id;
    }
    private static String getUsuarioId() {
        throw new RuntimeException("Id de usuario no encontrado");
    }    
}


java.lang.ExceptionInInitializerError
    at testing.NoClassDefFoundErrorPorInicializacionEstatica.main(NoClassDefFoundErrorPorInicializacionEstatica.java:23)
Caused by: java.lang.RuntimeException: Id de usuario no encontrado
    at testing.Usuario.getUsuarioId(NoClassDefFoundErrorPorInicializacionEstatica.java:41)
    at testing.Usuario.<clinit>(NoClassDefFoundErrorPorInicializacionEstatica.java:35)
    ... 1 more
java.lang.NoClassDefFoundError: Could not initialize class testing.Usuario
    at testing.NoClassDefFoundErrorPorInicializacionEstatica.main(NoClassDefFoundErrorPorInicializacionEstatica.java:23)

  1. Como NoClassDefFoundError es también un LinkageError, que se lanza debido a una dependencia en otra clase, si tu programa es dependiente de una librería nativa y el archivo .dll o .so no esta disponible. Recuerda que esto puede lanzar un java.lang.UnsatisfiedLinkError: no dll in java.library.path. Para solucionar este problema, asegurate de tener tu .dll o .so en tu jar.
  2. Si usas ANT, asegurate de depurar hasta que estés seguro que el script de ANT obtiene el valor correcto para el classpath, y que lo está anexando al manifest.mf correctamente.
  3. Un problema de permisos en tu archivo jar también puede causar un NoClassDefFoundError. Si estas corriendo tu programa Java en un sistema operativo multi-usuario como Linux y estas usando una librería compartida por múltiples aplicaciones que son ejecutadas por múltiples usuarios, quizás puedas estar en un problema de permisos, como que otro usuario sea el propietario de un jar y no es accesible en tu aplicación.
  4. Un error de tipéo en un XML de configuración puede ser otra causa para NoClassDefFoundError. Muchos frameworks de Java como Spring y Struts usan configuración XML específicamente para beans. Si escribiste mal el nombre del bean, puede ocurrir un java.lang.NoClassDefFoundError mientras se carga otra clase que tiene una dependencia en el bean incorrectamente nombrado.
  5. Otro ejemplo es cuando tu clase compilada está definida en un paquete no presenta el mismo paquete cuando es cargado, como en el caso de JApplet.
  6. Los classloaders multiples en entornos J2EE también pueden provocar este  java.lang.NoClassDefFoundError. Como J2EE no menciona una estructura estándar de classloader y depende de diferentes proveedores como Tomcat, WebLogic, o WebSphere sobre como cargan los diferentes componentes de J2EE como el archivo WAR o el archivo EJB-JAR. El classloader funciona en tres principios: Delegación, Visibilidad y Unicidad. Delegación significa que cada petición para cargar una clase es delegada al classloader padre. Visibilidad significa la habilidad de encontrar las clases que se cargan por el classloader, todos los classloader hijos pueden ver las clases cargadas por un classloader padre, pero un classloader padre no puede ver las clases cargadas por classloader hijos. Unicidad refuerza que la clase cargada por el padre nunca es recargada por los classloaders hijos. Ahora supón que una clase Usuario está presente tanto en el archivo WAR y EJB-JAR, y es cargada por el classloader de WAR, que es un hijo del classloader que carga las clases del EJB-JAR. Cuando un código en EJB-JAR se refiere a la clase Usuario, el classloader que cargó todas las clases de EJB no lo encuentra debido a que fue cargado por el classloader de WAR, que es su hijo. Esto resultará en un NoClassDefFoundError para la clase Usuario. También, si la clase está presente en ambos archivos jar  y llamas al método equals para comparar esos dos objetos, resultará en un ClassCastException debido a que los objetos cargados por diferentes classloaders no pueden ser iguales.
  7. También se puede obtener Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/tools/javac/Main, y esto ocurre porque tu classpath, PATH o JAVA_HOME no están configurados correctamente o la instalación de tu JDK no es correcta, lo que se resuelve reinstalando el JDK.
  8. Este error también puede ocurrir durante la vinculación que se produce durante la carga de clases en Java. Un ejemplo de este escenario es borrar la clase Usuario, de nuestro fragmento de código del punto 4, después de la compilación e intentar correr el programa. Ahora obtendrás un java.lang.NoClassDefFoundError diréctamente sin  java.lang.ExceptionInInitializerError y el mensaje será simplemente el nombre de la clase testing.Usuario.

 java.lang.NoClassDefFoundError: testing/Usuario
    at testing.NoClassDefFoundErrorPorInicializacionEstatica.main(NoClassDefFoundErrorPorInicializacionEstatica.java:23)

Si hay alguna otra razón por la que este error pueda aparecer, cuéntanos en los comentarios para documentarlo en beneficio de otros usuarios.

1 comentario:

  1. Error: no se ha encontrado o cargado la clase principal soft.say
    Causado por: java.lang.ClassNotFoundException: soft.say

    ResponderEliminar