Retirado deste belo tutorial da Sun:
Motivação
Os aplicativos escritos em linguagens de programação compiladas estaticamente, como C e C ++, são compilados em instruções nativas específicas da máquina e salvos como um arquivo executável. O processo de combinar o código em um código nativo executável é chamado de vinculação - a mesclagem do código compilado separadamente com o código da biblioteca compartilhada para criar um aplicativo executável. Isso é diferente nas linguagens de programação compiladas dinamicamente, como Java. Em Java, os arquivos .class gerados pelo compilador Java permanecem como estão até serem carregados na Java Virtual Machine (JVM) - em outras palavras, o processo de vinculação é executado pela JVM em tempo de execução. As classes são carregadas na JVM 'conforme necessário'. E quando uma classe carregada depende de outra classe, essa classe também é carregada.
Quando um aplicativo Java é iniciado, a primeira classe a ser executada (ou o ponto de entrada no aplicativo) é aquela com o método void estático público chamado main (). Essa classe geralmente tem referências a outras classes, e todas as tentativas de carregar as classes referenciadas são realizadas pelo carregador de classes.
Para ter uma idéia desse carregamento de classe recursivo, bem como da ideia de carregamento de classe em geral, considere a seguinte classe simples:
public class HelloApp {
public static void main(String argv[]) {
System.out.println("Aloha! Hello and Bye");
}
}
Se você executar esta classe especificando a opção de linha de comando -verbose: class, para que ela imprima quais classes estão sendo carregadas, você obterá uma saída com a seguinte aparência. Observe que essa é apenas uma saída parcial, pois a lista é muito longa para ser mostrada aqui.
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 você pode ver, as classes de tempo de execução Java exigidas pela classe de aplicativo (HelloApp) são carregadas primeiro.
Carregadores de classe na plataforma Java 2
A linguagem de programação Java continua evoluindo para facilitar a vida dos desenvolvedores de aplicativos todos os dias. Isso é feito fornecendo APIs que simplificam sua vida, permitindo que você se concentre na lógica de negócios, e não nos detalhes da implementação de mecanismos fundamentais. Isso é evidente pela recente mudança do J2SE 1.5 para o J2SE 5.0, a fim de refletir a maturidade da plataforma Java.
A partir do JDK 1.2, um carregador de classes de autoinicialização integrado à JVM é responsável por carregar as classes do tempo de execução Java. Esse carregador de classes carrega apenas as classes encontradas no caminho de classe de inicialização e, como essas são classes confiáveis, o processo de validação não é executado como nas classes não confiáveis. Além do carregador de classes de autoinicialização, a JVM possui um carregador de classes de extensão responsável por carregar classes de APIs de extensão padrão e um carregador de classes de sistema que carrega classes de um caminho de classe geral, bem como de suas classes de aplicativos.
Como há mais de um carregador de classes, eles são representados em uma árvore cuja raiz é o carregador de classes de autoinicialização. Cada carregador de classes tem uma referência ao seu carregador de classes pai. Quando um carregador de classes é solicitado a carregar uma classe, ele consulta seu carregador de classes pai antes de tentar carregar o item em si. O pai, por sua vez, consulta seu pai e assim por diante. Portanto, é somente depois que todos os carregadores de classes ancestrais não conseguem encontrar a classe que o carregador de classes atual se envolve. Em outras palavras, um modelo de delegação é usado.
A classe java.lang.ClassLoader
The java.lang.ClassLoader
é uma classe abstrata que pode ser subclassificada por aplicativos que precisam estender a maneira pela qual a JVM carrega dinamicamente classes. Os construtores em java.lang.ClassLoader
(e suas subclasses) permitem especificar um pai ao instanciar um novo carregador de classes. Se você não especificar explicitamente um pai, o carregador de classes do sistema da máquina virtual será atribuído como pai padrão. Em outras palavras, a classe ClassLoader usa um modelo de delegação para procurar classes e recursos. Portanto, cada instância do ClassLoader possui um carregador de classes pai associado, de modo que, quando solicitado a encontrar uma classe ou recursos, a tarefa é delegada ao seu carregador de classes pai antes de tentar encontrar a própria classe ou recurso. O loadClass()
método do ClassLoader executa as seguintes tarefas, em ordem, quando chamado para carregar uma classe:
Se uma classe já foi carregada, ela a retorna. Caso contrário, ele delega a pesquisa para a nova classe no carregador de classes pai. Se o carregador de classes pai não encontrar a classe, loadClass()
chame o método findClass()
para localizar e carregar a classe. O finalClass()
método procura a classe no carregador de classes atual se a classe não foi encontrada pelo carregador de classes pai.
Há mais no artigo original, que também mostra como implementar seus próprios carregadores de classes de rede, o que responde à sua pergunta sobre o porquê (e como). Veja também os documentos da API .