Tomado de este bonito tutorial de Sun:
Motivación
Las aplicaciones escritas en lenguajes de programación compilados estáticamente, como C y C ++, se compilan en instrucciones nativas específicas de la máquina y se guardan como un archivo ejecutable. El proceso de combinar el código en un código nativo ejecutable se llama vinculación: la fusión de código compilado por separado con código de biblioteca compartida para crear una aplicación ejecutable. Esto es diferente en lenguajes de programación compilados dinámicamente como Java. En Java, los archivos .class generados por el compilador de Java permanecen tal cual hasta que se cargan en la máquina virtual Java (JVM); en otras palabras, el proceso de vinculación lo realiza la JVM en tiempo de ejecución. Las clases se cargan en la JVM "según sea necesario". Y cuando una clase cargada depende de otra clase, entonces esa clase también se carga.
Cuando se inicia una aplicación Java, la primera clase que se ejecuta (o el punto de entrada a la aplicación) es la que tiene un método público de vacío estático llamado main (). Esta clase generalmente tiene referencias a otras clases, y todos los intentos de cargar las clases referenciadas son realizados por el cargador de clases.
Para tener una idea de esta carga recursiva de clases, así como la idea de carga de clases en general, considere la siguiente clase simple:
public class HelloApp {
public static void main(String argv[]) {
System.out.println("Aloha! Hello and Bye");
}
}
Si ejecuta esta clase especificando la opción de línea de comandos -verbose: class, de modo que imprima las clases que se están cargando, obtendrá un resultado similar al siguiente. Tenga en cuenta que esto es solo una salida parcial ya que la lista es demasiado larga para mostrar aquí.
prmpt>java -verbose:class HelloApp
[Opened C:\Program Files\Java\jre1.5.0\lib\rt.jar]
[Opened C:\Program Files\Java\jre1.5.0\lib\jsse.jar]
[Opened C:\Program Files\Java\jre1.5.0\lib\jce.jar]
[Opened C:\Program Files\Java\jre1.5.0\lib\charsets.jar]
[Loaded java.lang.Object from shared objects file]
[Loaded java.io.Serializable from shared objects file]
[Loaded java.lang.Comparable from shared objects file]
[Loaded java.lang.CharSequence from shared objects file]
[Loaded java.lang.String from shared objects file]
[Loaded java.lang.reflect.GenericDeclaration from shared objects file]
[Loaded java.lang.reflect.Type from shared objects file]
[Loaded java.lang.reflect.AnnotatedElement from shared objects file]
[Loaded java.lang.Class from shared objects file]
[Loaded java.lang.Cloneable from shared objects file]
[Loaded java.lang.ClassLoader from shared objects file]
[Loaded java.lang.System from shared objects file]
[Loaded java.lang.Throwable from shared objects file]
.
.
.
[Loaded java.security.BasicPermissionCollection from shared objects file]
[Loaded java.security.Principal from shared objects file]
[Loaded java.security.cert.Certificate from shared objects file]
[Loaded HelloApp from file:/C:/classes/]
Aloha! Hello and Bye
[Loaded java.lang.Shutdown from shared objects file]
[Loaded java.lang.Shutdown$Lock from shared objects file]
Como puede ver, las clases de tiempo de ejecución Java requeridas por la clase de aplicación (HelloApp) se cargan primero.
Cargadores de clases en la plataforma Java 2
El lenguaje de programación Java sigue evolucionando para facilitar la vida de los desarrolladores de aplicaciones todos los días. Esto se logra al proporcionar API que simplifican su vida al permitirle concentrarse en la lógica empresarial en lugar de los detalles de implementación de los mecanismos fundamentales. Esto es evidente por el cambio reciente de J2SE 1.5 a J2SE 5.0 para reflejar la madurez de la plataforma Java.
A partir de JDK 1.2, un cargador de clases de arranque integrado en la JVM es responsable de cargar las clases del tiempo de ejecución de Java. Este cargador de clases solo carga las clases que se encuentran en el classpath de arranque, y dado que estas son clases confiables, el proceso de validación no se realiza como para las clases no confiables. Además del cargador de clases bootstrap, la JVM tiene un cargador de clases de extensión responsable de cargar clases desde API de extensión estándar, y un cargador de clases de sistema que carga clases desde una ruta de clase general, así como sus clases de aplicación.
Como hay más de un cargador de clases, se representan en un árbol cuya raíz es el cargador de clases bootstrap. Cada cargador de clases tiene una referencia a su cargador de clases padre. Cuando se le pide a un cargador de clases que cargue una clase, consulta a su cargador de clases padre antes de intentar cargar el elemento en sí. El padre a su vez consulta a su padre, y así sucesivamente. Por lo tanto, solo después de que todos los cargadores de clases ancestrales no puedan encontrar la clase, el cargador de clases actual se involucra. En otras palabras, se utiliza un modelo de delegación.
La clase java.lang.ClassLoader
La java.lang.ClassLoader
es una clase abstracta que puede ser subclasificada por aplicaciones que necesitan extender la manera en que la JVM carga dinámicamente las clases. Los constructores en java.lang.ClassLoader
(y sus subclases) le permiten especificar un padre cuando crea una instancia de un nuevo cargador de clases. Si no especifica explícitamente un elemento primario, el cargador de clases del sistema de la máquina virtual se asignará como elemento primario predeterminado. En otras palabras, la clase ClassLoader usa un modelo de delegación para buscar clases y recursos. Por lo tanto, cada instancia de ClassLoader tiene un cargador de clases padre asociado, de modo que cuando se le solicita que encuentre una clase o recursos, la tarea se delega a su cargador de clases padre antes de intentar encontrar la clase o el recurso en sí. El loadClass()
método de ClassLoader realiza las siguientes tareas, en orden, cuando se llama para cargar una clase:
Si una clase ya se ha cargado, la devuelve. De lo contrario, delega la búsqueda de la nueva clase al cargador de clases padre. Si el cargador de clases padre no encuentra la clase, loadClass()
llama al método findClass()
para buscar y cargar la clase. El finalClass()
método busca la clase en el cargador de clases actual si el cargador de clases padre no encontró la clase.
Hay más en el artículo original, que también le muestra cómo implementar sus propios cargadores de clases de red, que responde a su pregunta de por qué (y cómo). Ver también los documentos API .