O carregamento de classes em Java envolve a leitura de arquivos .class em formato binário para a área de dados do tempo de execução (método), criando um objeto Java.lang.Class na região de heap. Esse objeto encapsula os dados da classe na área de método e fornece interfaces para acesso aos dados.
Existem várias formas de carregar classes:
- Diretamente do sistema local
- Baixar .class via rede
- Carregar de arquivos zip/jar
- Extrair de bancos de dados especializados
- Compilar dinamicamente .java para .class (servidores)
O processo de carregamento é dividido em três etapas: carregamento, ligação e inicialização. A ligação inclui:
- Validação: Garante a corretude da classe
- Preparação: Aloca memória para variáveis estáticas
- Resolução: Converte referências simbólicas em referências diretas
A inicialização atribui valores reais às variáveis estáticas. Por exemplo, para private static int a = 10, a sequência é: a=0 (padrão) → a=10 (inicialização).
A ordem de inicialização é: ( variáveis estáticas, blocos estáticos ) > ( variáveis, blocos de inicialização ) > construtor
A hierarquia de carregadores inclui:
- Carregador Bootstrap: Implementado em C++, carrega bibliotecas core como rt.jar
- Carregador de Extensão: Carrega classes de JAVA_HOME/jre/lib/ext
- Carregador de Aplicativo: Carrega classes do classpath
- Carregador Personalizado: Implementado por frameworks como Tomcat
O modelo de delegação garante que:
- O carregador verifica primeiro em seu pai
- Evita carregamento duplicaod
- Previne substituição de classes críticas
Para obter a hierarquia de carregadores:
ClassLoader carregador = TesteCarregadorClasse.class.getClassLoader();
while(carregador != null) {
System.out.println(carregador);
carregador = carregador.getParent();
}
A verificação de igualdade entre classes requer:
- Nome da classe idêntico
- Mesmo carregador
Para garantir carregamento único, o método loadClass implementa:
protected synchronized Class<?> loadClass(String nomeClasse, boolean resolve)
throws ClassNotFoundException {
// Verifica se já foi carregado
Class<?> classe = findLoadedClass(nomeClasse);
if (classe == null) {
try {
if (parent != null) {
classe = parent.loadClass(nomeClasse, false);
} else {
classe = findBootstrapClass0(nomeClasse);
}
} catch (ClassNotFoundException e) {
classe = findClass(nomeClasse);
}
}
if (resolve) {
resolveClass(classe);
}
return classe;
}
Implementação de carregador personalizado:
public class MeuCarregador extends ClassLoader {
protected Class<?> findClass(String nomeClasse) {
byte[] bytes = lerBytesDaFonte(nomeClasse);
return defineClass(nomeClasse, bytes, 0, bytes.length);
}
private byte[] lerBytesDaFonte(String nomeClasse) {
// Lógica para obter bytes da classe
}
}