Entrevistas de Java backend cobrem a semântica central da linguagem, collections, concorrência, Spring e como você raciocina sobre a JVM. Abaixo estão as perguntas mais frequentes, cada uma com uma resposta modelo.
Cada pergunta vem com uma resposta modelo para você comparar com a sua.
1
Como o HashMap funciona internamente, e no que o ConcurrentHashMap se diferencia?
Resposta
O HashMap guarda entradas em um array de buckets indexado pelo hash da chave; colisões formam uma lista encadeada que vira uma árvore rubro-negra depois de oito nós. Ele não é thread-safe — um resize concorrente pode corromper a estrutura. O ConcurrentHashMap consegue thread safety travando apenas o bucket que está sendo escrito (CAS mais synchronized no nó), em vez do mapa inteiro, então as leituras são lock-free e as escritas só disputam dentro do mesmo bucket.
2
Explique o contrato de equals() e hashCode(). O que quebra se você o violar?
Resposta
Se dois objetos são iguais por equals(), eles devem retornar o mesmo hashCode; objetos diferentes podem compartilhar um hash, mas idealmente não deveriam. Violar isso quebra qualquer coleção baseada em hash: um objeto guardado num HashMap cai em um bucket, e uma busca posterior calcula um hash diferente, procura em outro bucket e reporta que a entrada não existe. O bug clássico é mudar um campo usado no hashCode depois de inserir o objeto num set.
3
O que é garbage collection e quais coletores a JVM oferece?
Resposta
O GC libera memória do heap para objetos que não são mais alcançáveis a partir dos GC roots. O heap é geracional — a maioria dos objetos morre jovem, então as coletas menores na young generation são baratas. Os coletores incluem Serial e Parallel para throughput, G1 como padrão moderno que equilibra tempo de pausa e throughput, e ZGC ou Shenandoah para heaps muito grandes com pausas de submilissegundos.
4
Qual é a diferença entre exceções checked e unchecked?
Resposta
Exceções checked estendem Exception e precisam ser declaradas ou tratadas — representam condições recuperáveis que quem chama deveria prever, como IOException. Unchecked estendem RuntimeException e sinalizam erros de programação, como NullPointerException ou IllegalArgumentException. A orientação prática é usar unchecked para bugs e checked com moderação, porque obrigar todo chamador a tratar uma exceção costuma gerar blocos catch vazios.
5
O que é Dependency Injection no Spring e quais tipos de injeção existem?
Resposta
DI significa que um objeto recebe suas dependências do container em vez de construí-las, o que desacopla classes e as torna testáveis. O Spring suporta injeção por construtor, por setter e por campo. A injeção por construtor é a preferida: as dependências viram final, o objeto nunca fica meio construído, e a classe não consegue esconder uma lista de dependências crescente do jeito que a injeção por campo permite.
6
Qual é a diferença entre @Component, @Service, @Repository e @Controller?
Resposta
As quatro registram a classe como um bean do Spring e são tecnicamente intercambiáveis para o scanning. Elas diferem na intenção e nos extras que o Spring agrega: @Repository traduz exceções de persistência para a hierarquia DataAccessException do Spring, @Controller e @RestController participam do mapeamento de requisições, e @Service é uma marcação semântica para lógica de negócio. Usar a anotação específica documenta a camada e ativa o comportamento certo.
7
O que é uma transação? Explique os níveis de isolamento.
Resposta
Uma transação é uma unidade de trabalho que satisfaz ACID: ou faz commit por completo ou dá rollback. Os níveis de isolamento trocam consistência por concorrência — Read Uncommitted permite dirty reads, Read Committed as evita, Repeatable Read também evita leituras não repetíveis, e Serializable evita phantom reads ao custo de throughput. A maioria dos sistemas roda em Read Committed e resolve as anomalias restantes com locking otimista.
8
Qual é a diferença entre ArrayList e LinkedList? Quando usar cada um?
Resposta
ArrayList é baseado em um array redimensionável: o acesso por índice é O(1), mas inserir ou remover no meio desloca elementos. LinkedList é uma lista duplamente encadeada: inserir e remover em um nó conhecido é O(1), mas o acesso por índice é O(n) e cada elemento carrega overhead de nó. Na prática o ArrayList vence quase sempre porque a localidade de cache da CPU supera a complexidade teórica; LinkedList é útil principalmente como Deque.
9
O que são volatile e synchronized? Quando usar cada um?
Resposta
volatile garante visibilidade — uma escrita fica imediatamente visível para outras threads e as leituras nunca são cacheadas — mas não garante atomicidade, então um contador volatile ainda perde incrementos. synchronized garante visibilidade e exclusão mútua ao mesmo tempo, então só uma thread executa o bloco por vez. Use volatile para uma flag simples lida por muitas threads, e synchronized ou uma classe Atomic quando a operação é read-modify-write.
10
Explique o problema N+1 no Hibernate e como evitá-lo.
Resposta
O N+1 acontece quando você carrega N entidades pai com uma query e depois acessa lazy uma coleção em cada uma, gerando N queries adicionais. Geralmente aparece como uma página rápida nos testes com três linhas e inutilizável com três mil. As soluções são um JOIN FETCH em JPQL, um entity graph, batch fetching via @BatchSize, ou uma projeção que selecione exatamente os campos que a view precisa.
11
Qual é a diferença entre String, StringBuilder e StringBuffer?
Resposta
String é imutável, então cada concatenação aloca um objeto novo — construir uma string em loop com + é quadrático. StringBuilder é um buffer mutável e é a ferramenta certa para esse loop. StringBuffer é basicamente o mesmo que StringBuilder, mas com métodos synchronized; é praticamente legado, já que compartilhar um buffer mutável entre threads é raro e geralmente precisa de um locking mais grosso de qualquer forma.
12
O que é a Stream API do Java e quando você não deveria usá-la?
Resposta
Streams expressam operações sobre coleções de forma declarativa, como um pipeline de operações intermediárias lazy e uma operação terminal que dispara a execução. Elas brilham em filtragem, mapeamento e agregação porque a intenção continua legível. Evite-as em loops muito quentes, onde a abstração custa tempo mensurável, em lógica com efeitos colaterais, e em qualquer caso em que um for simples ficaria mais claro.