O Destino e o Novo Começo — Encerrando a Jornada Completa

O Destino e o Novo Começo — Encerrando a Jornada Completa

Nesta última aula você olha para o caminho inteiro, do primeiro "Olá, mundo" às três trilhas. Também escreve um programa que faz o inventário dos seus projetos para montar um portfólio, vê para onde o Java está indo depois do Java 25 e sai com um plano concreto para os próximos meses.
Java

• • 21 min de leitura

Nesta última aula você olha para o caminho inteiro, do primeiro "Olá, mundo" às três trilhas. Também escreve um programa que faz o inventário dos seus projetos para montar um portfólio, vê para onde o Java está indo depois do Java 25 e sai com um plano concreto para os próximos meses.

Setenta e oito aulas atrás, este curso começou do zero: nenhuma linha de código, nenhuma suposição sobre o que você já sabia. De lá para cá, você escreveu um servidor HTTP a partir de sockets, colocou uma aplicação web com banco de dados no ar num servidor, fez um app de desktop e um de Android conversarem com a sua API, e publicou um jogo que outra pessoa baixa e joga sem instalar nada. Esta aula não traz tecnologia nova. Ela faz três coisas que costumam ficar de fora dos cursos: mostra o que tudo isso tem em comum, transforma o que você construiu em algo que outras pessoas conseguem ver, e aponta os próximos destinos.

O mapa do caminho percorrido

O curso teve uma espinha dorsal e três trilhas de extensão. Cada uma terminou num projeto que junta tudo o que veio antes:

Parte O que você construiu As ideias que ficaram
Trilha principal (53 aulas) a linguagem, do zero à concorrência; o Farol, um servidor HTTP escrito sobre sockets, com roteador, arquivos estáticos, keep-alive, limites e desligamento gracioso tipos e objetos; records, tipos selados e pattern matching; coleções e streams; exceções e recursos; testes; threads virtuais; o protocolo HTTP lido byte a byte; jlink e jpackage
Trilha A (14 aulas): web, API e banco as Trilhas da Ilha: SQL, JDBC, Spring Boot, JPA, Flyway, PostgreSQL, MySQL e MongoDB, a API REST de "Extensão — A API das Trilhas: REST, Validação, Erros e OpenAPI", segurança com JWT, testes com Testcontainers, o site com htmx e o deploy de "Extensão — Do Laptop ao Servidor: Docker, nginx e Deploy" o que os frameworks fazem por dentro (montamos miniaturas de injeção de dependências, de contexto de persistência e de migrações); transações e concorrência no banco; nunca confiar na entrada
Trilha B (4 aulas): desktop e Android "Extensão — Um App Desktop que Consome a API (MVVM)" em JavaFX, e "Extensão — Java no Android: o que Mudou com o Kotlin e um App que Consome a API" separar a interface do estado (MVVM); a mesma API servindo vários clientes; threads e interface gráfica
Trilha C (7 aulas): jogos do game loop à mão até A Ilha dos Blocos, o plataforma de "O Capstone dos Jogos — o Plataforma Completo e Exportado" passo fixo; colisão; física própria e com Box2D; medir alocação; ciclo de vida de recursos nativos

Olhando a tabela de longe, o que mais se repete não são as bibliotecas, porque as bibliotecas mudam. O que se repete são meia dúzia de princípios. Eles apareceram em todas as trilhas, com roupas diferentes:

  1. Regras longe da entrada e da saída. O Roteador do Farol, as regras das Trilhas da Ilha e a Fase do plataforma não conhecem sockets, HTTP nem GPU, e por isso foram testados em milissegundos.
  2. Todo recurso tem dono. O close do socket, a conexão do banco devolvida ao pool, o dispose da textura, o free do Pool. Os erros mais difíceis do curso, como a falha de segmentação do Box2D e o objeto liberado duas vezes, foram sempre de dono indefinido.
  3. Desconfie da entrada. O cabeçalho HTTP malicioso, o caminho com ../, o SQL injetado e o arquivo de preferências editado à mão são o mesmo problema.
  4. Meça antes de mudar. Os 74 bytes por passo do Asteroids, as 501 consultas do N+1, o pior quadro caindo exatamente numa pausa do coletor. Nenhuma dessas conclusões saiu de intuição.
  5. Entenda um nível abaixo. Escrevemos o HTTP antes do Spring, o Proxy antes do Spring Data e o game loop antes do libGDX. Quem já viu por dentro usa o framework sem medo, e sabe onde procurar quando ele falha.
  6. Pronto é quando outra pessoa consegue usar. O Farol virou uma imagem jlink, as Trilhas foram para um servidor, e o jogo virou um executável.

Olá, mundo — de novo

O primeiro programa do curso foi uma classe Ola, com public static void main(String[] args) e System.out.println. Ainda na mesma aula, ele encolheu para a forma compacta, que continua sendo um programa Java 25 completo:

void main() {
    IO.println("Olá, mundo!");
}
$ java OlaMundo.java
Olá, mundo!

Na primeira aula, cada palavra aqui era um ato de fé. Hoje você consegue explicar tudo o que está implícito:

  • É um arquivo-fonte compacto: o compilador envolve o código numa classe sem nome.
  • O main pode ser de instância, sem static e sem String[] args, porque o lançador cria o objeto antes de chamá-lo.
  • IO é uma classe de java.lang, que está sempre importado.
  • O comando java OlaMundo.java compila em memória e executa, sem gerar nenhum .class no disco.
  • Por baixo, há o bytecode, a JVM, o interpretador e o JIT que depois compila o trecho quente, e o coletor que vai limpar a String quando ninguém mais a usar.

A mesma linha, só que agora sem mistério. Se os acentos saírem como ? num terminal configurado sem UTF-8, rode com java -Dstdout.encoding=UTF-8 OlaMundo.java.

Um inventário da jornada

Um portfólio começa por saber o que você tem. Este programa percorre as pastas dos seus projetos e monta uma tabela em Markdown, pronta para um README, com o tamanho de cada projeto, quantos testes ele tem e alguns sinais do Java moderno que ele usa. Ele também é um arquivo-fonte compacto, e cabe tudo do curso num só lugar: records, streams, expressões regulares, try com recursos e exceções verificadas dentro de lambdas.

/// Inventário da Jornada: percorre as pastas dos seus projetos e monta uma tabela em Markdown
/// com o tamanho de cada um e alguns sinais do Java moderno que ele usa.
///
/// Uso: java Inventario.java Farol=~/cursos/farol Jogo=~/cursos/ilha-dos-blocos > PORTFOLIO.md
///
/// Arquivo-fonte compacto (Java 25): sem "public class", sem "public static", e o módulo
/// java.base inteiro já importado — como no primeiro "Olá, mundo" do curso.

/// O que medimos em cada projeto.
record Projeto(String nome, int arquivos, int linhas, int testes, int records, int selados, int switchesNovos) {
    Projeto somar(Projeto o) {
        return new Projeto("**Total**", arquivos + o.arquivos, linhas + o.linhas, testes + o.testes,
                records + o.records, selados + o.selados, switchesNovos + o.switchesNovos);
    }
}

/// O que medimos em cada arquivo (um Projeto é a soma deles).
record Contagem(int linhas, int testes, int records, int selados, int switchesNovos) { }

// padrões compilados uma vez: um record declarado, um tipo selado, um "case ... ->"
final Pattern RECORD = Pattern.compile("\\brecord\\s+\\w+\\s*[(<]");
final Pattern SELADO = Pattern.compile("\\bsealed\\s+(interface|class|abstract)");
final Pattern CASE_SETA = Pattern.compile("\\bcase\\b.*->");
final Set<String> IGNORAR = Set.of("target", "build", "out", "outt", ".git", ".gradle");

void main(String[] args) throws IOException {
    if (args.length == 0) {
        IO.println("uso: java Inventario.java Nome=pasta [Nome=pasta ...]");
        return;
    }
    List<Projeto> projetos = new ArrayList<>();
    for (String arg : args) {
        String[] partes = arg.split("=", 2);
        if (partes.length != 2) throw new IllegalArgumentException("esperado Nome=pasta, veio: " + arg);
        projetos.add(medir(partes[0], Path.of(partes[1])));
    }
    projetos.sort(Comparator.comparingInt(Projeto::linhas).reversed());   // o maior primeiro

    IO.println("| Projeto | Arquivos .java | Linhas de código | Testes | records | tipos selados | case -> |");
    IO.println("|---|---:|---:|---:|---:|---:|---:|");
    Projeto total = new Projeto("", 0, 0, 0, 0, 0, 0);
    for (Projeto p : projetos) {
        IO.println(linha(p));
        total = total.somar(p);
    }
    if (projetos.size() > 1) IO.println(linha(total));
}

String linha(Projeto p) {
    return "| %s | %d | %,d | %d | %d | %d | %d |".formatted(
            p.nome(), p.arquivos(), p.linhas(), p.testes(), p.records(), p.selados(), p.switchesNovos());
}

Projeto medir(String nome, Path raiz) throws IOException {
    if (!Files.isDirectory(raiz)) throw new NoSuchFileException(raiz.toString());
    try (Stream<Path> caminhos = Files.walk(raiz)) {                   // o Stream segura a pasta aberta: feche-o
        List<Contagem> contagens = caminhos
                .filter(c -> c.toString().endsWith(".java"))
                .filter(c -> !ignorado(raiz.relativize(c)))
                .map(this::contar)
                .toList();
        return new Projeto(nome, contagens.size(),
                contagens.stream().mapToInt(Contagem::linhas).sum(),
                contagens.stream().mapToInt(Contagem::testes).sum(),
                contagens.stream().mapToInt(Contagem::records).sum(),
                contagens.stream().mapToInt(Contagem::selados).sum(),
                contagens.stream().mapToInt(Contagem::switchesNovos).sum());
    }
}

/// Pastas geradas pela compilação não são "seu código".
boolean ignorado(Path relativo) {
    for (Path parte : relativo) if (IGNORAR.contains(parte.toString())) return true;
    return false;
}

Contagem contar(Path arquivo) {
    int linhas = 0, testes = 0, records = 0, selados = 0, cases = 0;
    boolean emComentario = false;
    try {
        for (String bruta : Files.readAllLines(arquivo)) {
            String s = bruta.strip();
            if (emComentario) {                                         // dentro de /* ... */
                if (s.contains("*/")) emComentario = false;
                continue;
            }
            if (s.startsWith("/*")) { emComentario = !s.contains("*/"); continue; }
            if (s.isEmpty() || s.startsWith("//")) continue;            // "///" (Markdown doc) também é comentário
            linhas++;
            if (s.startsWith("@Test")) testes++;
            if (RECORD.matcher(s).find()) records++;
            if (SELADO.matcher(s).find()) selados++;
            if (CASE_SETA.matcher(s).find()) cases++;
        }
    } catch (IOException e) {
        throw new UncheckedIOException("não consegui ler " + arquivo, e);   // lambdas não lançam checked
    }
    return new Contagem(linhas, testes, records, selados, cases);
}

Alguns detalhes que foram assunto de aulas inteiras:

  • Os comentários /// são o formato de documentação em Markdown do Java 23+, e o próprio inventário os trata como comentário.
  • O Files.walk devolve um Stream que mantém diretórios abertos. Sem o try com recursos, cada projeto medido deixaria descritores de arquivo vazando até o coletor passar, se passar.
  • contar é usado dentro de um map, e lambdas não podem lançar exceções verificadas. A IOException é embrulhada numa UncheckedIOException, que preserva a causa original.
  • IGNORAR impede de contar código gerado. Sem ele, os .java que o Maven ou o Gradle copiam para target/ e build/ seriam contados duas vezes.

Rodei o inventário em quatro dos projetos que construí para este curso:

java Inventario.java "Farol (servidor HTTP)=farol" "Painel desktop (JavaFX)=painel" \
                     "A Ilha dos Blocos (libGDX)=ilha-dos-blocos" "Asteroids=asteroids" > PORTFOLIO.md
Projeto Arquivos .java Linhas de código Testes records tipos selados case ->
Farol (servidor HTTP) 33 1,812 67 18 3 27
A Ilha dos Blocos (libGDX) 19 1,113 16 2 0 17
Painel desktop (JavaFX) 15 677 7 7 0 4
Asteroids 6 419 8 1 0 0
Total 73 4,021 98 28 3 48

Os separadores de milhar saíram com vírgula porque a máquina está em inglês. O %,d respeita o idioma do sistema, e num sistema em português sai 1.812. Com uma pasta que não existe, o programa para com NoSuchFileException: /nao/existe, em vez de gerar uma tabela com zeros enganosos.

Os números dizem pouco sozinhos. O que eles mostram é o formato do trabalho: o Farol tem um teste para cada 27 linhas, porque um servidor exposto à rede não pode ter comportamento adivinhado. Tem também 18 records e 3 hierarquias seladas, porque o protocolo HTTP é feito de dados imutáveis com poucas variantes. O jogo tem proporcionalmente menos testes, e é exatamente o ponto: só as regras (a física, o progresso, as fases) foram testadas, e a parte que desenha foi verificada com os olhos e com fotos.

Do inventário ao portfólio

Um recrutador, ou você mesmo daqui a um ano, vai gastar poucos minutos em cada projeto. Um repositório de portfólio precisa responder rápido a quatro perguntas:

  1. O que é isto? Uma frase e uma imagem: a captura do jogo, o site das Trilhas no ar, o curl respondido pelo Farol.
  2. Como eu rodo? Um comando só. mvn verify, ./gradlew lwjgl3:run, docker compose up ou o executável do jpackage para baixar.
  3. Funciona? Testes rodando automaticamente a cada push, com o selo verde no topo do README.
  4. Por que foi feito assim? Uma seção "Decisões" com duas ou três escolhas e o que foi trocado por elas. Por exemplo: "resolvemos a colisão um eixo de cada vez para evitar a ambiguidade das quinas" ou "PostgreSQL como banco principal pela busca textual em português". Essa seção separa quem copiou um tutorial de quem entendeu o problema.

A terceira pergunta é respondida por uma integração contínua. No GitHub, um arquivo em .github/workflows/testes.yml basta para um projeto Maven:

name: testes

on:
  push:
    branches: [ main ]
  pull_request:

jobs:
  testes:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-java@v5
        with:
          distribution: temurin
          java-version: '25'
          cache: maven
      - name: Compilar e testar
        run: mvn --batch-mode verify

A cada push, uma máquina limpa baixa o código, instala o Java 25 e roda todos os testes. É a versão automatizada da regra de "Extensão — Bancos de Verdade nos Testes: Testcontainers": se o teste só passa na sua máquina, ele não prova nada. Nos projetos com Testcontainers, a máquina do GitHub já tem Docker, e os testes contra um PostgreSQL de verdade rodam ali também.

Três projetos bem apresentados valem mais que trinta pela metade, e este curso deixa exatamente três de peso:

  • o Farol, que mostra fundamentos: rede, protocolo, concorrência e segurança;
  • as Trilhas da Ilha, que mostram o dia a dia do mercado: Spring, bancos, API, segurança e deploy;
  • A Ilha dos Blocos, que mostra projeto de software fora do caminho comum, e um produto que se instala e se usa.

Como o Java continua andando

O Java que você aprendeu é o Java 25, lançado em setembro de 2025. Desde o Java 10, em 2018, sai uma versão nova a cada seis meses, e a cada dois anos uma delas recebe suporte longo (LTS): 17, 21 e 25. Se o ritmo se mantiver, a próxima LTS será o JDK 29, em setembro de 2027. Enquanto isso, as versões intermediárias trazem novidades que vão amadurecendo em preview.

O JDK 27 saiu em 15 de setembro de 2026, com nove JEPs. Três delas conversam direto com este curso:

  • JEP 534, Compact Object Headers by Default. O cabeçalho dos objetos cai de 12 para 8 bytes na configuração padrão. Lembra da conta de bytes por partícula em "Sem Lixo no Loop — Pools, ECS e o Coletor nos Jogos"? Ela muda, e o exercício 4 refaz essa conta.
  • JEP 523, Make G1 the Default Garbage Collector in All Environments. O G1 passa a ser o padrão também nas máquinas pequenas, onde antes a JVM escolhia o Serial.
  • JEP 533, Structured Concurrency (Seventh Preview). O StructuredTaskScope que usamos nas aulas de concorrência continua em preview, ainda sendo refinado.

E o próximo grande passo já tem data: a JEP 401, Value Objects (Preview), foi marcada para o JDK 28. É o primeiro resultado do Projeto Valhalla, e são objetos sem identidade, que a JVM pode guardar "achatados" na memória, como os arrays de float que venceram a disputa de desempenho na aula de partículas, mas escritos como classes normais. Quando ela sair do preview, muitos records do Farol e do jogo serão candidatos naturais a virar value records.

Para acompanhar sem se afogar:

  • openjdk.org/jeps é a fonte primária. Toda mudança da linguagem e da JVM começa numa JEP, com motivação, alternativas descartadas e exemplos.
  • inside.java reúne os anúncios, podcasts e textos da equipe da Oracle que desenvolve o Java.
  • dev.java tem os tutoriais oficiais, atualizados a cada versão.
  • Um hábito simples: a cada versão nova, leia a lista de JEPs e rode os seus três projetos nela. Adicionar '27' ao lado de '25' numa matriz do setup-java faz isso de graça no CI.

Os próximos destinos

Terminar o curso não é chegar. É ter o mapa para escolher o próximo lugar. Os caminhos mais naturais a partir daqui:

  • Back-end e nuvem. A continuação direta da Trilha A: aprofundar o Spring (Spring Modulith para organizar monólitos, mensageria com Kafka ou RabbitMQ), observabilidade com OpenTelemetry, Kubernetes para quando um servidor com Docker não basta, e a leitura de Designing Data-Intensive Applications, de Martin Kleppmann, que é o livro sobre o que acontece quando os dados não cabem numa máquina.
  • A JVM por dentro. Se as aulas de coletor, JIT e alocação foram as suas preferidas: o JFR a fundo, o JMH para benchmarks confiáveis e o código-fonte do próprio OpenJDK, que é aberto e bem comentado.
  • Android. A Trilha B mostrou que o Android moderno fala Kotlin. Como Kotlin roda na mesma JVM e usa as mesmas bibliotecas, ele é o passo seguinte natural, e a transição leva semanas, não meses.
  • Jogos. Continuar no libGDX (atlas de texturas, áudio, controles, as extensões FreeType e Box2DLights) ou levar os mesmos conceitos a um motor com editor. O game loop, o passo fixo e a disciplina de recursos são iguais em todos.
  • Código aberto. O libGDX, o Spring, o próprio OpenJDK e milhares de bibliotecas menores aceitam contribuições. Comece por documentação, exemplos ou um bug marcado como "good first issue". Ler o código de outras pessoas e ter o seu revisado ensina o que curso nenhum ensina.
  • Certificação. A Oracle oferece o exame Oracle Certified Professional: Java SE 25 Developer (1Z0-831). Ele cobra detalhes da linguagem e da biblioteca padrão num nível de minúcia que o dia a dia não cobra. Não substitui projetos, mas é um bom complemento se o seu mercado valoriza certificações.

Um plano possível para os próximos 90 dias, em vez de "estudar mais":

  • Mês 1: publicar os três projetos com README, capturas, a seção "Decisões" e CI verde. Rodar o inventário e colocar a tabela num repositório de apresentação.
  • Mês 2: escolher um destino da lista acima e fazer uma coisa nova nele, que o curso não fez. Por exemplo, a API das Trilhas publicando eventos no Kafka, ou uma terceira fase do jogo com inimigos.
  • Mês 3: uma contribuição para um projeto de código aberto que você usou, e um texto (no seu site, no LinkedIn, onde for) explicando uma coisa que você aprendeu construindo. Explicar é a prova final de ter entendido.

Fechando a jornada

Quando este curso começou, a aula de abertura contou a história de uma linguagem criada no começo dos anos 1990 por uma equipe liderada por James Gosling, pensada para aparelhos eletrônicos e que acabou dominando servidores, celulares e, pelo caminho, um dos jogos mais vendidos da história. Trinta anos depois, o Java continua sendo reescrito por dentro (threads virtuais, records, pattern matching e, em breve, objetos de valor) sem quebrar o código que já existe. Essa combinação de mudança constante e compatibilidade é rara, e é a razão pela qual o que você aprendeu aqui vai continuar valendo por muitos anos.

Mas o que fica deste ano não é uma linguagem. É um jeito de trabalhar:

  • ler a especificação antes de adivinhar;
  • escrever o teste que prova a regra;
  • medir antes de otimizar;
  • saber quem é dono de cada recurso;
  • desconfiar de toda entrada;
  • considerar pronto só o que outra pessoa consegue usar.

Isso vale em Java, em Kotlin, em Go, no próximo framework que aparecer e no que ainda nem foi inventado.

O Farol foi o nome do primeiro grande projeto porque um farol não leva ninguém a lugar nenhum. Ele mostra onde estão as pedras, e o caminho quem escolhe é o navegante. Este curso tentou ser isso. O destino chegou, e ele é só o ponto de partida do próximo começo, que agora é seu.

Fontes e leituras recomendadas

Exercícios

Exercício 1

Acrescente ao inventário uma coluna "threads virtuais", que conta as linhas que usam Thread.ofVirtual, Thread.startVirtualThread ou newVirtualThreadPerTaskExecutor. Quais partes do programa mudam?

Ver resposta

✓ Resposta: Mudam quatro pontos, sem mexer na estrutura. (a) Um padrão novo: final Pattern VIRTUAL = Pattern.compile("ofVirtual|startVirtualThread|newVirtualThreadPerTaskExecutor");. (b) Um componente novo, int virtuais, nos dois records, Contagem e Projeto, e a soma correspondente em somar. (c) Em contar, a linha if (VIRTUAL.matcher(s).find()) virtuais++; e o novo campo no return. (d) Em medir, mais um mapToInt(Contagem::virtuais).sum(), e em main/linha, a coluna nova no cabeçalho e no formatted. O compilador ajuda: ao acrescentar um componente ao record, todo new Projeto(...) com o número antigo de argumentos deixa de compilar, e nenhum lugar fica esquecido.

Exercício 2

O inventário tem um bug de contagem com linhas como /* ajuste fino */ int limite = 8;. Qual é o erro, e como corrigir?

Ver resposta

✓ Resposta: A linha começa com /* e contém */, então o código cai no if (s.startsWith("/*")), faz emComentario = false e executa continue. A declaração int limite = 8; que vem depois do comentário nunca é contada. A correção é olhar o que sobra depois do fechamento: if (s.startsWith("/*")) { int fim = s.indexOf("*/"); if (fim < 0) { emComentario = true; continue; } s = s.substring(fim + 2).strip(); if (s.isEmpty()) continue; }, e seguir com a contagem normal. Um contador realmente correto também precisaria ignorar // e /* dentro de strings. Para isso existem analisadores de verdade, como o do próprio javac (Compiler Tree API), e o exercício mostra por que eles existem.

Exercício 3

Por que medir usa try (Stream<Path> caminhos = Files.walk(raiz)), se streams comuns, como lista.stream(), nunca são fechados? O que aconteceria sem o try ao medir centenas de projetos?

Ver resposta

✓ Resposta: A maioria dos streams é só um cálculo sobre dados em memória e não segura nada. O Files.walk é diferente: ele abre diretórios no sistema operacional à medida que desce na árvore, e os DirectoryStreams abertos só são liberados quando o stream é fechado. Sem o try, cada chamada deixaria descritores abertos até o coletor rodar os limpadores, e o coletor não tem pressa, porque não sabe que descritores são escassos. Com centenas de projetos, o processo pode esbarrar no limite do sistema (Too many open files). É a mesma regra do curso inteiro: um recurso do sistema operacional tem dono, e o dono fecha.

Exercício 4

No JDK 27, com os cabeçalhos compactos ligados por padrão (JEP 534), o cabeçalho de um objeto cai de 12 para 8 bytes. Refaça a estimativa do exercício 1 de "Sem Lixo no Loop — Pools, ECS e o Coletor nos Jogos" para a versão "objetos" com o G1 (referências de 4 bytes, alinhamento em 8).

Ver resposta

✓ Resposta: O Vector2 passa de 12 + 8 = 20 → 24 bytes para 8 + 8 = 16 bytes, já alinhado. A Particula passa a ter 8 + 4 + 4 + 4 = 20 → 24 bytes, como antes, porque o alinhamento absorve a economia. O total por partícula cai de 72 para 24 + 2 × 16 = 56 bytes, e 56 × 1.500 = 84.000 bytes por quadro, em vez de 108.000: 22% menos alocação, sem mudar uma linha do jogo. É uma boa ilustração de como o Java melhora por baixo do código existente, e também um lembrete de que números de memória medidos numa versão precisam ser remedidos na seguinte. Medido no JDK 27: a versão "objetos" aloca 89.739 bytes por quadro com o G1, os 84.000 da conta mais os mesmos 5.739 bytes que não dependem das partículas.

Exercício 5

Você vai colocar as Trilhas da Ilha em produção num servidor da empresa. Use o Java 25 ou o JDK 27, lançado em setembro de 2026? Justifique, e diga como aproveitar o JDK 27 mesmo assim.

Ver resposta

✓ Resposta: Em produção, o Java 25. Ele é LTS, com correções de segurança por muitos anos, e o Spring, o Hibernate, os drivers e as imagens Docker estão validados nele. O JDK 27 é uma versão intermediária: a Oracle dá atualizações para ele só até março de 2027, quando ele é substituído pelo 28, e adotá-lo obriga a atualizar a cada seis meses. Isso é possível, mas é uma decisão de operação, não de gosto. Para aproveitar o 27 sem risco: ponha-o na matriz do CI (java-version: ['25', '27']), para descobrir cedo qualquer incompatibilidade e medir ganhos como os cabeçalhos compactos, e use-o nos projetos pessoais e de estudo. Quando o JDK 29 LTS sair, a migração vai ser um evento sem sustos, porque os testes já terão rodado em todas as versões do meio.

Comentários

Mais em Java

A Máquina por Trás da Máquina — Class Loading, Pilha, Heap e JIT
A Máquina por Trás da Máquina — Class Loading, Pilha, Heap e JIT

O que a JVM faz com o seu programa: class loaders e delegação, a inicialização…

Quando o Contrato se Quebra — Exceções Verificadas e Não Verificadas
Quando o Contrato se Quebra — Exceções Verificadas e Não Verificadas

Como ler um stack trace, capturar exceções com try e catch, garantir a limpeza…

Mundos em Blocos — Mapas de Tiles e um Jogo de Plataforma
Mundos em Blocos — Mapas de Tiles e um Jogo de Plataforma

Nesta aula você desenha uma fase no formato do editor Tiled, carrega esse mapa…