Diferenças entre Versões do Java e Conceitos Essenciais para Entrevistas

Diferenças Fundamentais entre Versões do Java

Java 8

Introduziu suporte a programação funcional com Expressões Lambda e a API Stream para processamento fluente de coleções. A nova API de data e hora (LocalDate, LocalDateTime) substituiu as antigas classes Date/Calendar. Métodos default em interfaces e a classe Optional foram adicionados para melhorar a flexibilidade e o tratamento de nulos.

Java 11 (LTS)

Trouxe inferência de tipos para variáveis locais com a palavra-chave var. O novo HttpClient API suporta HTTP/2 e WebSocket. A classe String recebeu novos métodos como isBlank() e strip(). Módulos legados como Java EE e CORBA foram remvoidos.

Java 17 (LTS)

Introduziu Classes Seladas (sealed classes) para restringir heranças, Pattern Matching para instanceof, Records como classes de dados imutáveis e Text Blocks para strings multilinha. Os coletores de lixo ZGC e Shenandoah também foram incorporados.

Java 21 (LTS)

Apresenta Threads Virtuais (virtual threads) para concorrência de alta performance, Pattern Matching para a instrução switch, Coleções Sequenciadas (sequenced collections) e uma versão aprimorada do ZGC baseada em gerações.

Por que o Spring Boot 3.x Requer Java 17+

O Spring Boot 3.x é construído sobre o Spring Framework 6, que foi projetado para explorar as características modernas do Java 17, como Records, Pattern Matching e Classes Seladas. Veja como esses recursos melhoram o desenvolvimento:

Record (Classes de Registro)

Representam uma forma concisa de criar classes de dados imutáveis.


// Abordagem Tradicional (Exemplo Conceitual)
public class RespostaConsulta {
    private final String identificador;
    private final Object resultado;
    private final long momento;

    public RespostaConsulta(String identificador, Object resultado, long momento) {
        this.identificador = identificador;
        this.resultado = resultado;
        this.momento = momento;
    }

    // Necessita getters, equals, hashCode, toString explicitamente...
}

// Com Java 17+ (Record)
public record RespostaConsulta(String identificador, Object resultado, long momento) { }

Pattern Matching

Simplifica verificações de tipo e conversões explícitas.


// Código Tradicional (Conceitual)
public Object manipular(Object objeto) {
    if (objeto instanceof String) {
        String texto = (String) objeto;
        return processarTexto(texto);
    }
    // ... outros blocos
    return null;
}

// Com Pattern Matching (Java 16+)
public Object manipular(Object objeto) {
    if (objeto instanceof String texto) {
        return processarTexto(texto); // Conversão implícita
    }
    // ... outros blocos
    return null;
}

Classes Seladas (Sealed Classes)

Definem hierarquias de herança controladas em tempo de compilação.


// Define uma hierarquia selada
public sealed class RespostaApi
    permite RespostaSucesso, RespostaErro, RespostaAviso {
    // ...
}

// Somente estas classes podem estender
public final class RespostaSucesso extends RespostaApi { /* ... */ }
public final class RespostaErro extends RespostaApi { /* ... */ }
public final class RespostaAviso extends RespostaApi { /* ... */ }
// Impede a criação de subclasses não autorizadas.

Switch Expressions (Expressões Switch)

Permitem que a instrução switch seja usada como expressão.


// Tradicional (Conceitual)
public String obterTipo(Object obj) {
    if (obj instanceof String) {
        return "texto";
    } else if (obj instanceof Integer) {
        return "numero";
    }
    // ...
    return "desconhecido";
}

// Com Expressão Switch (Java 14+)
public String obterTipo(Object obj) {
    return switch (obj) {
        case String s -> "texto";
        case Integer i -> "numero";
        default -> "desconhecido";
    };
}

Text Blocks (Blocos de Texto)

Simplificam a declaração de strings de múltiplas linhas.


// Tradicional
String sql = "SELECT u.id, u.nome\n" +
             "FROM usuarios u\n" +
             "WHERE u.status = 'ATIVO'";

// Com Text Blocks (Java 15+)
String sql = """
        SELECT u.id, u.nome
        FROM usuarios u
        WHERE u.status = 'ATIVO'
        """;

Expressões Lambda e API Stream vs. Laços For Tradicionais

As Expressões Lambda e a API Stream oferecem sintaxe concisa, melhor legibilidade e suporte nativo a operações paralelas. A composição funcional e a inferência de tipos também reduzem o código. Contudo, laços for tradicionais podem oferecer performance ligeiramente superior em cenários simples e facilitam a depuração passo a passo.

Alocação de Memória de Pilha para Threads Java

A JVM aloca um tamanho fixo de pilha (geralmente 512KB-1MB) para cada thread, mesmo que o uso real seja menor. As razões incluem:

  • Imprevisibilidade do Tamanho do Quadro: O espaço necessário por método não pode ser determinado estaticamente.
  • Desempenho: Reduz a sobrecarga de verificações de limite a cada chamada de método.
  • Suporte a Chamadas Profundas: Frameworks e aspectos (AOP, transações) podem criar pilhas de 20-50 chamadas.
  • Simplicidade de Implementação: Tamanhos fixos simplificam o gerenciamento de memória pela JVM.
  • Memória Virtual: A alocação é de endereços virtuais; a memória física só é consumida conforme o uso real (paginação).

Tipos de Laços For em Java e Suas Características

Laço For Indexado (Tradicional)

Vantagens: Permite acesso ao índice, controle preciso do fluxo (break, continue) e modificação direta dos elementos via set(). Geralmente oferece o melhor desempenho para listas de acesso aleatório.

Desvantagens: Código mais verboso e propenso a erros de limites ao remover elementos.

For-Each (Aprimorado)

Vantagens: Sintaxe extremamente concisa e elimina erros de indexação.

Desvantagens: Sem acesso ao índice, impossível modificar ou remover elementos durante a iteração.

Iterador (Iterator)

Vantagens: Permite remoção segura de elementos (iterator.remove()) durante a iteração. Funciona com qualquer Collection.

Desvantagens: Apenas iteração unidirecional e código mais verboso.

Stream forEach (Funcional)

Vantagens: Extremamente conciso com lambdas, fácil composição e suporte a processamento paralelo.

Desvantagens: Não permite controle de fluxo (break), modificação ou remoção de elementos.

ListIterator (Específico para Listas)

Vantagens: Iteração bidirecional, e capacidade de adicionar, modificar e remover elementos. Provê acesso ao índice.

Desvantagens: Limitado a List e código mais extenso.

Problemas Comuns com Cache Redis e Soluções

Penetração de Cache (Cache Penetration)

Definição: Requisições para dados que não existem, tanto no cache quanto no banco de dados, forçando todas as requisições a consultarem o banco.

Soluções: Cachear objetos nulos ou vazios, usar filtros de Bloom para verificar a existência antes de acessar o cache/banco, ou implementar limitação de taxa na API.

Colapso de Cache (Cache Breakdown)

Definição: Um chave popular (hot key) expira, fazendo com que múltiplas requisições simultâneas atinjam o banco de dados.

Soluções: Usar um bloqueio ditsribuído (mutex) para que apenas uma thread reconstrua o cache, ou implementar expiração lógica (onde os dados antigos são servidos enquanto o cache é atualizado em background).

Avalanche de Cache (Cache Avalanche)

Definição: Muitas chaves expiram simultaneamente ou o servidor Redis fica indisponível, sobrecarregando o banco de dados.

Soluções: Adicionar um tempo aleatório aos TTLs (Time-To-Live) para evitar expiração em massa, implementar uma arquitetura de cache multinível (local + Redis) para alta disponibilidade, e usar mecanismos de limitação e degradação (fallback) como proteção final.

Tags: java-17 java-21 records pattern-matching sealed-classes

Publicado em 7-8 16:27