java.lang.ClassNotFoundException

Java

Esta excepción esta muy relacionada con el artículo anterior java.lang.NoClassDefFoundError, Caused by: java.lang.ClassNotFoundException.

A menudo nos encontraremos ambas excepciones, pero en ocasiones solo nos encontraremos con java.lang.ClassNotFoundException.

El motivo de esta excepción es prácticamente el mismo. En la documentación nos dice que esta excepción se produce cuando el ClassLoader intenta leer la descripción de una clase, y la definición de dicha clase no es encontrada.

Estamos ante el mismo motivo visto en el artículo anterior. La definición de la clase no se encuentra debido a que la librería que lo contiene no esta en el classpath de la aplicación, o bien, si estamos ante un servidor de aplicaciones, la librería no está o el classloader que intenta cargarla no tiene visible esa librería. En cualquier caso tenemos que repasar la configuración de nuestras aplicaciones para solucionar este tipo de errores.

En los servidores de aplicaciones es más común encontrarnos con estas excepciones, ya que existen varios ClassLoader diferentes para una aplicación, y distinta políticas de carga de clases. Tenemos un ClassLoader para el servidor, uno para librerías comunes a todas las aplicaciones, y un ClassLoader para cada aplicación. La jerarquía normalmente es esta aunque depende del servidor de aplicaciones. Así por ejemplo si se delega la carga de clases al classloader del servidor, y la librería con definición de la clase se encuentra en la aplicación (es decir en el WEB-INF/lib) al final obtendremos esta excepción o la que comentábamos en el artículo anterior.

Vamos a reproducir esta excepción y así veremos como es el código que provoca estos errores.

Creación de la estructura del proyecto

mkdir a
mkdir b
mkdir main

Creación de la clase A.java

La creamos en el directorio o paquete a.

package a;
public class A {
    public void echo() {
        System.out.println("Hello from A !");
    }
}

Creación de la clase B.java

La creamos en el directorio o paquete b.

package b;
public class B {
    public void echo() {
        System.out.println("Hello from B !");
    }
}

Creación de la clase MainClass.java

Esta clase hace uso de las dos anteriores, y la creamos en el directorio o paquete main.

package main;

import b.*;
import a.*;
public class MainClass {
    public static final void main(String argv[]) {
        A a = new A();
        a.echo();
        try {
            Class bclass = Class.forName("b.B");
            B b = (B) bclass.newInstance();
            b.echo();
        } catch(Throwable ex) {
            ex.printStackTrace();
        }
    }
}

Compilamos los fuentes

Nos situamos en el directorio raiz y compilamos

$ javac a*.java b*.java main*.java

Ejecutamos la clase principal

$ java -cp . main.MainClass
Hello from A !
Hello from B !

Provocamos la excepción java.lang.ClassNotFoundException

La forma mas sencilla es renombrar el directorio b por otro nombre, y así la clase B ya no es encontrara por la Jvm, ya que deja de existir el package b.

Ejecutamos de nuevo la aplicación y obtendremos la excepción esperada.

$ java -cp . main.MainClass
Hello from A !
java.lang.ClassNotFoundException: b.B
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown S
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Unknown Source)
        at main.MainClass.main(MainClass.java:10)

En esta ocasión el error no se produce en la creación de la clase (new B()), si no cuando intentamos leer su definición

Class bclass = Class.forName("b.B");