← tópicos

Específicos · Engenharia de Software

Java: JVM/JDK/JRE, POO, coleções, exceções checked × unchecked, streams, Spring

Antes de julgar qualquer item, pergunte que palavra a definição descreve — encapsulamento, herança e polimorfismo são coisas distintas, e a banca troca uma pela outra mais do que faz qualquer…

Altíssima89 itens no tópico

A ideia que organiza o assunto

Java nasceu de uma indireção. O compilador javac não gera código de máquina: gera bytecode, instruções de uma máquina que não existe fisicamente. Quem existe é a JVM, escrita uma vez para cada plataforma real, que lê esse bytecode e o executa. O .class é o mesmo no Windows, no Linux e no mainframe; o que muda é a JVM debaixo dele. Dessa indireção decorre tudo o que a linguagem tem de característico — portabilidade, ausência de ponteiros, coletor de lixo, tempo de partida mais alto.

Mas não é essa a ideia que resolve a prova. Olhe o que a CEBRASPE de fato pergunta neste tópico e você encontra, na maioria esmagadora dos itens, orientação a objetos escrita com sintaxe Java — e, dentro dela, um único formato repetido à exaustão: a definição está correta e o nome que a assina está trocado. “Abstração é a propriedade que permite que um método tenha comportamentos distintos conforme os parâmetros” descreve sobrecarga. “Encapsulamento é o princípio pelo qual objetos de uma mesma superclasse invocam métodos de mesma assinatura com comportamentos distintos” descreve polimorfismo. Nenhuma dessas frases tem erro de conteúdo — têm erro de etiqueta.

Daí o método: leia a definição primeiro, decida sozinho que palavra ela nomeia e só então olhe o substantivo do item. Se você ler o nome antes, ele contamina a leitura e a frase parece coerente. Lido por último, o nome errado salta. Esse único hábito decide mais itens deste tópico do que saber Java.

A segunda metade do tópico são listagens de código pedindo o resultado. Também ali a regra é ler na ordem certa: execute o código com papel antes de olhar o valor que o item anuncia, porque o valor anunciado é sempre plausível.

Por que se usa (e o que custa)

Ganha-se portabilidade sem recompilação e memória gerenciada. O programador não libera objeto, não faz aritmética de endereço e não corrompe o processo com liberação dupla — e é essa ausência de ponteiro que torna o coletor de lixo possível, porque ele pode mover objetos no heap sem invalidar nada. Ganha-se também o ecossistema corporativo que vem junto: Java EE padronizou transação, mensageria, persistência e componentes distribuídos, e o Spring ofereceu uma alternativa mais leve à mesma lista de problemas.

Paga-se em três moedas. A indireção da JVM custa tempo de inicialização e consumo de memória — problema real o bastante para existir o GraalVM native image, que compila antecipadamente para binário nativo e inverte exatamente essas duas grandezas. O coletor automático custa pausas em momentos que você não escolhe. E a verbosidade é concreta: foram precisas as lambdas e a Stream API do Java 8 para escrever em uma linha uma transformação de coleção.

A formulação honesta para a prova: Java troca controle sobre a máquina por previsibilidade e portabilidade. Você não escolhe onde o objeto mora nem quando ele morre.

Como funciona

JDK, JRE e JVM são três círculos concêntricos. A JVM executa bytecode e abriga o JIT e o coletor de lixo. O JRE é a JVM mais as bibliotecas padrão — o suficiente para executar. O JDK é o JRE mais as ferramentas de desenvolvimento (javac, jar, javadoc, jdb) — o necessário para compilar. O caminho é .javajavac.class → JVM → instruções nativas, com o JIT promovendo a código de máquina os trechos mais executados. Para distribuir, empacota-se em JAR, que é um ZIP com manifesto e aceita qualquer arquivo dentro, inclusive sem relação com Java.

Os quatro pilares, cada um com uma frase que não serve para nenhum outro:

Herança simples, interfaces à vontade. Uma classe estende no máximo uma outra — decisão tomada para evitar a ambiguidade do diamante — mas implementa quantas interfaces quiser. É por isso que interfaces são descritas como a saída para contornar a restrição de herança única, e é por isso que elas permitem tratar polimorficamente classes sem parentesco algum: basta o tipo comum. Desde o Java 8 a interface aceita método concreto (default e static), o que permite evoluir uma interface já publicada sem quebrar quem a implementa. E todo campo de interface é implicitamente public static final — atribuir valor a ele depois nem compila.

Arrays e coleções. Em Java, int[][] é um array de arrays: cada linha é um objeto próprio, e por isso linhas de tamanhos diferentes são legais. Nas coleções, o que decide item é o contrato: List mantém ordem de inserção e aceita repetição; Set recusa duplicata e o HashSet não promete ordem nenhuma; Map associa chave única a valor. Passar um array (ou qualquer objeto) a um método passa uma cópia da referência: reatribuir o parâmetro não afeta o chamador, mas alterar o conteúdo apontado afeta.

Exceções. Tudo desce de Throwable, que se divide em Error (falha da JVM, não se trata) e Exception. As checkedIOException, SQLException, ClassNotFoundException — são condições externas previsíveis e o compilador exige try/catch ou throws. As unchecked descem de RuntimeExceptionNullPointerException, ArithmeticException, ArrayIndexOutOfBoundsException — são defeitos de programação e o compilador não cobra tratamento. O bloco finally executa em qualquer desfecho, com ou sem exceção, e é onde se libera recurso.

Lambdas e streams entraram no Java 8, não saíram dele. Um stream encadeia operações sobre uma sequência: filter seleciona, map aplica uma função a todos os elementos, sorted ordena, reduce e collect fecham a conta. As intermediárias são preguiçosas — nada executa até chegar uma terminal.

O ecossistema que o edital cobra junto. Em JDBC, PreparedStatement é pré-compilado, é a escolha padrão e é a defesa contra injeção de SQL. Em JPA/Hibernate, a fábrica é única e longa (SessionFactory, EntityManagerFactory) e a sessão é curta, uma por unidade de trabalho; @Version habilita o bloqueio otimista e @Transient exclui da persistência. Em Java EE, o session bean é síncrono e o message-driven bean é o assíncrono. No Spring, @Controller marca o componente web e @RequestMapping diz qual requisição ele trata; o Spring Boot autoconfigura e embute o servidor; o Spring Cloud traz os padrões de sistema distribuído, com Eureka para descoberta e Zuul como gateway de camada 7.

O que decide os itens

Os quatro pilares, sem sobreposição — a distinção que decide mais itens que todas as outras somadas:

se o enunciado fala em…a palavra é
representar só o relevante, ignorar o restoabstração
reunir dado e código, ocultar o interior, acesso por métodoencapsulamento
reaproveitar de uma classe-pai, extends, propagar alteraçãoherança
mesma assinatura, comportamentos diferentespolimorfismo

Sobrecarga × sobrescrita — metade dos itens de polimorfismo é só isto:

o que mudaonderesolvido em
sobrecarga (overloading)lista de parâmetros diferentemesma classecompilação — ligação estática
sobrescrita (overriding)mesma assinatura, corpo novosubclasseexecução — ligação tardia

O tipo de retorno não entra na assinatura: dois métodos que só diferem nele não compilam.

Herança × interface:

extendsimplements
quantasuma classevárias interfaces
herda estado?simnão (só constantes)
serve parareuso de implementaçãocontrato e tipo comum

Polimorfismo não exige superclasse comum — basta tipo comum, que pode ser interface. E herança simples × múltipla se decide pelo número de superclasses diretas, não pelo número de membros efetivamente herdados.

Componente → papel, no ecossistema:

énão é
Tomcat, Jettycontêiner de servlets e JSPservidor de aplicação; não roda EJB
GlassFish, WildFly, JBoss EAPservidor de aplicação JEE completocontêiner web apenas
Hibernateimplementação de ORMa especificação (essa é a JPA)
Hibernate Enversauditoria e versionamento de entidadesidentidade, SSO, autenticação
JGroupscomunicação em grupo entre nós do clustergestão de usuários
.htaccessconfiguração por diretório do Apacherecurso do NGINX
Swaggerferramental em torno da OpenAPIa própria especificação
Eurekadescoberta de serviços por registro e heartbeatgateway
Zuulgateway de borda em camada 7balanceador de camada 4
MapStructgera o mapeador em tempo de compilaçãomapeador por reflexão em execução

Retorno dos métodos JDBCexecuteQuery() devolve ResultSet, executeUpdate() devolve int com as linhas afetadas, execute() devolve boolean.

Edições da plataforma — SE é a base e o desktop (Swing, AWT); EE é o servidor e a web (servlet, JSP, EJB, JPA) e é extensão da SE, não alternativa; ME é o dispositivo com memória, vídeo e processamento limitados, inclusive set-top box e leitor de blu-ray.

Checked × unchecked — checked descende de Exception sem passar por RuntimeException e o compilador obriga a tratar; unchecked descende de RuntimeException e não obriga. Divisão inteira por zero lança ArithmeticException, que é unchecked; divisão de double por zero não lança nada e devolve Infinity.

Nos itens com listagem de código, o que decide quase sempre é uma destas cinco coisas: o caso-base da recursão (devolve 0 ou 1?); o nome enganoso da variável do for-each (size continua sendo o elemento); print × println; a++ × ++a; e a referência compartilhada quando um array é passado a um método.

Números que caem

byte · short · int · long8 · 16 · 32 · 64 bits
float · double32 · 64 bits
char16 bits, UTF-16, sem sinal
tipos primitivos, ao todo8 (String não é um deles)
faixa do int−2³¹ a 2³¹−1
classes que se pode estender · interfaces que se pode implementar1 · ilimitadas
índices de arrayde 0 a length − 1
níveis de acesso4: privatepackage (padrão) → protectedpublic
capacidade inicial do ArrayList · do HashMap10 · 16, com fator de carga 0,75
Java 5genéricos, enum, anotações, autoboxing
Java 8lambda, Stream API, método default em interface
Java 11 · 17 · 21versões LTS; 17 consolidou record e sealed, 21 trouxe virtual threads
finally executasempre (salvo System.exit ou parada da JVM)

Como a CEBRASPE derruba você aqui

Troca de termo — quase 40% dos itens errados, e metade deles entre os pilares de OO. “Abstração é a propriedade que permite que um método de determinado nome tenha comportamentos distintos” — é sobrecarga. “O encapsulamento em Java consiste no princípio” pelo qual objetos de uma mesma superclasse invocam métodos de mesma assinatura com comportamentos distintos — é polimorfismo. “Na herança por especificação”, quando a subclasse apenas acrescenta e mantém inalterada a superclasse — é herança por extensão. Polimorfismo entre objetos “de superclasses diferentes” — sem tipo comum não há polimorfismo. “A herança é do tipo simples” quando o item descreve uma classe com duas superclasses. A outra metade troca o nome de um artefato pelo vizinho de prateleira: @RequestClass no lugar de @RequestMapping; um executeUpdate “cujos valores de retorno serão tratados por um ResultSet”; DriverManager.getConnection recebendo a classe do driver em vez da URL JDBC; a Session descrita com a vida da SessionFactory — “instanciado apenas uma vez na aplicação”, “durante todo o tempo de execução”; Java que “utiliza ponteiros para o gerenciamento de endereços de memória”, quando o que existe são referências; objetos alocados “no heap nativo do sistema operacional”; extends apresentado como anotação, com “a substituição dos caracteres #XPTO por @extends”. A tabela dos quatro pilares é a defesa da primeira metade e a tabela de artefatos, a da segunda: leia a definição, escolha você a palavra, confira o nome por último.

Inversão — um quarto dos itens errados. Uma regra da linguagem afirmada ao contrário: Java “permite herança múltipla de classes” (de classes é simples; de interfaces é múltipla); o polimorfismo com “métodos de assinaturas diferentes, e a escolha do método ocorre em tempo de compilação”; a chamada polimórfica que “tem sua invocação resolvida em tempo de compilação do código”, quando a ligação é tardia; os membros da subclasse “automaticamente sobrepostos e eliminados pelos da classe-pai Veiculo”, com a seta da herança ao contrário; “as expressões lambda terem sido excluídas do Java 8”, quando foram introduzidas nele. Dois produtos trocados de lugar: “a JSE é bastante utilizada no desenvolvimento web, especialmente em aplicações que utilizam HTTP”. Um comportamento revertido: a virtual thread que “bloqueia a platform thread subjacente”, quando se desmonta da carrier; a imagem compilada em AOT que “consomem mais memória e, como consequência, possuem um tempo de inicialização maior”. E uma recomendação virada do avesso: o PreparedStatement cujo uso “deve ser feito em casos específicos”, quando é o padrão, e classes que “devem colaborar e trocar mensagens com a maior parte das outras classes do mesmo sistema”, que é o oposto de baixo acoplamento. Diante de uma direção afirmada, formule a direção oposta e veja qual das duas você consegue justificar.

Resultado de código alterado — um em cada nove. O valor anunciado é sempre plausível, e é isso que engana. Foo.x = 20 sobre um campo de interface não compila, mas o item diz que “apresentará 20 como resultado”. Um fatorial de 5 vale 120 e o item promete que “retornará o resultado numérico 20”. Um for (int size : x) sobre {1, 3, 7, 22, 51} imprime 1372251, e o item diz que o resultado “será 6”, porque você leu size como tamanho. A recursão de fun(3) cujo caso-base devolve 0 soma 3 + 2 + 0 e vale 5, e o item conta com um caso-base igual a 1. Nunca aceite o número: identifique primeiro o papel de cada trecho — caso-base, escopo do bloco static, pré-incremento contra pós-incremento — e só depois some.

Atribuição errada dentro do ecossistema. Ação real, produto errado: “GlassFish e TomCat são exemplos de servidores de aplicação JEE que suportam a tecnologia EJB” — o Tomcat é contêiner de servlets; o Envers que “permite realizar gerenciamento de identidade por meio de JPA no WildFly ou JBoss” — Envers faz auditoria de entidades; o JGroups “destinada a gerenciar grupos de usuários com as respectivas aplicações em seus bancos de dados” — faz comunicação em grupo entre nós; o .htaccess “utilizado de forma nativa no servidor NGINX” — é do Apache; o Spring Boot que existe “a fim de otimizar seu código, a partir de pequenos ajustes e trocas para deixar mais rápido o resultado do código” — é autoconfiguração. Quando a função descrita for plausível, confira se ela é daquele produto ou do vizinho de prateleira.

Generalização e restrição inventada. O mapeamento que “exige que todas as propriedades dos objetos de origem e destino tenham exatamente o mesmo nome” — convenção sobre configuração admite configuração explícita. O bloqueio otimista que atua “independentemente de controle de versão aplicado à entidade” — sem @Version não há o que comparar. A subclasse que pode acrescentar membros “desde que mantenha intactos os membros herdados” — pode sobrescrever. “A inicialização de um array bidimensional deve ter todos os elementos com as mesmas quantidades” — em Java o array de arrays é irregular por natureza. E as instâncias que compartilham os atributos da classe “assim como os conteúdos desses atributos” — a estrutura é da classe, o estado é de cada objeto. Diante de todo, sempre, apenas, independentemente de e desde que, procure o caso que a palavra exclui.

Causa inventada. Pouco frequente, mas limpa quando aparece: o código “não será compilado, pois há dois métodos construtores” — dois construtores são sobrecarga, e sobrecarga é legal; a empresa terá problemas “devido ao fato de o Hibernate ser considerado inapropriado para a execução de trabalhos em uma arquitetura altamente escalável” — a premissa é falsa. Item que justifica um porquê pede segunda leitura no porquê, não na conclusão.

Erros clássicos

Achar que a JVM compila e o JDK executa. É o contrário: o javac, que vem no JDK, compila para bytecode; a JVM executa o bytecode. Quem só vai executar não precisa do JDK. E a portabilidade está no bytecode — a JVM é justamente a parte que existe em versão diferente para cada plataforma.

Achar que Java tem ponteiros. Tem referências: sem aritmética de endereço, sem conversão para inteiro, sem liberação manual.

Confundir o heap da JVM com o heap do sistema operacional. O coletor gerencia a memória da própria máquina virtual. Item que fale em coletar objetos “no heap nativo do sistema operacional” trocou a camada.

Confundir sobrecarga com sobrescrita. Parâmetros diferentes é sobrecarga, decidida na compilação; assinatura idêntica na subclasse é sobrescrita, decidida na execução. Se o item juntar “polimorfismo” e “tempo de compilação” na mesma frase, um dos dois está fora do lugar.

Achar que Set guarda a ordem de inserção. O HashSet não guarda ordem nenhuma; o que ele garante é ausência de duplicatas. Ordem de inserção é do LinkedHashSet, ordenação é do TreeSet.

Tratar NullPointerException como checked. Tudo que desce de RuntimeException é unchecked. O compilador só cobra tratamento das checked.

Esquecer que passar um array a um método passa a referência. O método recebe uma cópia da referência, não do conteúdo: alterar A[0] lá dentro altera o array do chamador.

Chamar Session de objeto único da aplicação. Único é a SessionFactory; a Session nasce e morre com a unidade de trabalho.

Confundir o @Transient da JPA com o transient da linguagem. A anotação exclui da persistência; a palavra reservada exclui da serialização.

Praticar87 itens