Uma classe interna não estática é definida dentro de outra classe sem o modificador static. Diferentemente de classes regulares, ela mantém uma referência implícita à instância da classe externa que a criou.
Características principais:
- Objetos de classes internas não estáticas existem apenas como parte de objetos da classe externa
- Têm acesso direto a todos os membros da classe externa, incluindo privados
- A classe externa não pode acessar membros da classe interna diretamente
- Não podem conter membros estáticos como métodos, atributos ou blocos de inicialização estáticos
Acesso a membros:
Dentro de uma classe interna, usamos this para referenciar membros da própria classe. Para acessar membros da classe externa, utilizamos NomeClasseExterno.this.
Exemplo prático:
class Empresa {
private String nome = "TechCorp";
public void exibirInfo() {
System.out.println(nome);
}
public class Funcionario {
private String nome = "João";
public void apresentar() {
System.out.println(nome); // João
System.out.println(Empresa.this.nome); // TechCorp
}
}
}
public class Main {
public static void main(String[] args) {
Empresa.Funcionario func = new Empresa().new Funcionario();
func.apresentar();
}
}
Classes Inetrnas Estáticas
Uma classe interna estática é definida com o modificador static dentro de outra classe. Ela se comporta como um membro estático da classe externa.
Características principais:
- Pode ser instanciada sem necessidade de uma instância da classe externa
- Só tem acesso a membros estáticos da classe externa
- É carregada na memória de forma independente da classe externa
- É útil para agrupar classes que têm uma relação conceitual forte
Exemplo prático:
class Database {
private int porta = 3306;
private static String driver = "MySQL";
public static class Configuracao {
public void exibirDriver() {
System.out.println(driver); // Funciona
// System.out.println(porta); // Erro de compilação
}
}
}
public class Main {
public static void main(String[] args) {
Database.Configuracao config = new Database.Configuracao();
config.exibirDriver();
}
}
Classes Anônimas
Classes anônimas são úteis quando precisamos de uma implementação única e descartável, sem necessidade de nomear a classe formalmente. São muito utilizadas em listeners de eventos, calbacks e implementações rápidas de interfaces.
Características principais:
- Não têm nome definido
- Podem implementar uma interface ou estender uma classe
- Têm acesso limitado ao escopo externo em alguns contextos
- São идеais para operações pontuais
Exemplo prático:
interface Processador {
void executar();
}
public class Main {
public void processar(Processador p) {
p.executar();
}
public static void main(String[] args) {
Main app = new Main();
app.processar(new Processador() {
@Override
public void executar() {
System.out.println("Primeira implementação");
}
});
app.processar(new Processador() {
@Override
public void executar() {
System.out.println("Segunda implementação");
}
});
}
}
Classes Locais
Classes locais são definidas dentro de um método, tendo seu escopo restrito exclusivamente a esse método. São raramente utilizadas em projetos reais devido às suas limitações de visibilidade.
Características principais:
- Visibilidade restrita ao método onde são definidas
- Não podem ter modificadores de acesso
- Podem acessar membros finais ou efetivamente finais do método
- Úteis quando a lógica é extremamente específica daquele método
Exemplo prático:
public class Gerenciador {
public void iniciar() {
class Timer {
public void iniciarContagem() {
System.out.println("Iniciando contagem...");
}
}
Timer cronometro = new Timer();
cronometro.iniciarContagem();
}
public static void main(String[] args) {
new Gerenciador().iniciar();
}
}