Tratto da questo bel tutorial di Sun:
Motivazione
Le applicazioni scritte in linguaggi di programmazione compilati staticamente, come C e C ++, vengono compilate in istruzioni native specifiche della macchina e salvate come file eseguibile. Il processo di combinazione del codice in un codice nativo eseguibile è chiamato collegamento: l'unione del codice compilato separatamente con il codice della libreria condivisa per creare un'applicazione eseguibile. Ciò è diverso nei linguaggi di programmazione compilati dinamicamente come Java. In Java, i file .class generati dal compilatore Java rimangono così come sono fino a quando non vengono caricati nella Java Virtual Machine (JVM) - in altre parole, il processo di collegamento viene eseguito dalla JVM in fase di esecuzione. Le classi vengono caricate nella JVM su una base 'secondo necessità'. E quando una classe caricata dipende da un'altra classe, anche quella classe viene caricata.
Quando viene avviata un'applicazione Java, la prima classe da eseguire (o il punto di ingresso nell'applicazione) è quella con metodo vuoto statico pubblico chiamato main (). Questa classe di solito ha riferimenti ad altre classi e tutti i tentativi di caricare le classi di riferimento vengono eseguiti dal programma di caricamento classi.
Per avere un'idea di questo caricamento di classe ricorsivo e dell'idea di caricamento di classe in generale, considera la seguente semplice classe:
public class HelloApp {
public static void main(String argv[]) {
System.out.println("Aloha! Hello and Bye");
}
}
Se si esegue questa classe specificando l'opzione della riga di comando -verbose: class, in modo che stampa quali classi vengono caricate, si otterrà un output simile al seguente. Si noti che questo è solo un output parziale poiché l'elenco è troppo lungo per essere mostrato qui.
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]
Come puoi vedere, le classi di runtime Java richieste dalla classe di applicazione (HelloApp) vengono caricate per prime.
Caricatori di classi nella piattaforma Java 2
Il linguaggio di programmazione Java continua a evolversi per semplificare la vita degli sviluppatori di applicazioni ogni giorno. Questo viene fatto fornendo API che ti semplificano la vita permettendoti di concentrarti sulla logica aziendale piuttosto che sui dettagli di implementazione dei meccanismi fondamentali. Ciò è evidente dal recente passaggio da J2SE 1.5 a J2SE 5.0 al fine di riflettere la maturità della piattaforma Java.
A partire da JDK 1.2, un caricatore di classi bootstrap incorporato in JVM è responsabile del caricamento delle classi del runtime Java. Questo caricatore di classi carica solo le classi che si trovano nel percorso di classe di avvio e poiché si tratta di classi affidabili, il processo di convalida non viene eseguito come per le classi non attendibili. Oltre al caricatore di classi bootstrap, JVM dispone di un caricatore di classi di estensioni responsabile del caricamento di classi dalle API di estensioni standard e di un caricatore di classi di sistema che carica le classi da un percorso di classe generale e dalle classi dell'applicazione.
Poiché esiste più di un caricatore di classi, sono rappresentati in un albero la cui radice è il caricatore di classi bootstrap. Ogni caricatore di classi ha un riferimento al proprio caricatore di classi principale. Quando viene richiesto a un caricatore di classi di caricare una classe, consulta il proprio caricatore di classi principale prima di tentare di caricare l'elemento stesso. Il genitore a sua volta consulta il suo genitore e così via. Quindi è solo dopo che tutti i caricatori di classi di antenati non riescono a trovare la classe che viene coinvolto l'attuale caricatore di classi. In altre parole, viene utilizzato un modello di delega.
La classe java.lang.ClassLoader
La java.lang.ClassLoader
è una classe astratta che può essere sottoclassi dalle applicazioni che necessitano di estendere il modo in cui la JVM dinamicamente classi carichi. I costruttori in java.lang.ClassLoader
(e le sue sottoclassi) consentono di specificare un genitore quando si crea un'istanza di un nuovo caricatore di classi. Se non si specifica esplicitamente un genitore, il caricatore di classi di sistema della macchina virtuale verrà assegnato come genitore predefinito. In altre parole, la classe ClassLoader utilizza un modello di delega per cercare classi e risorse. Pertanto, ogni istanza di ClassLoader ha un programma di caricamento classi padre associato, in modo che quando viene richiesto di trovare una classe o risorse, l'attività viene delegata al programma di caricamento classe padre prima di tentare di trovare la classe o la risorsa stessa. Il loadClass()
metodo di ClassLoader esegue le seguenti attività, in ordine, quando viene chiamato per caricare una classe:
Se una classe è già stata caricata, la restituisce. Altrimenti, delega la ricerca della nuova classe al programma di caricamento classi genitore. Se il programma di caricamento classi genitore non trova la classe, loadClass()
chiama il metodo findClass()
per trovare e caricare la classe. Il finalClass()
metodo cerca la classe nel programma di caricamento classi corrente se la classe non è stata trovata dal programma di caricamento classi padre.
C'è di più nell'articolo originale, che mostra anche come implementare i propri caricatori di classi di rete, che risponde alla tua domanda sul perché (e come). Vedi anche i documenti API .