Java não está morrendo — está renascendo
\nTodos os anos alguém escreve "Java está morto". E todos os anos o Java prova o contrário. Em 2025, a linguagem que já tem quase 30 anos recebe várias atualizações fundamentais. Se você programa em Java, mas olhou o changelog pela última vez há um ano — já está atrasado. Vamos analisar o que exatamente vai mudar e como essas mudanças afetarão seu código.
\nNão vou sobrecarregá-lo com especificações secas. Em vez disso — exemplos reais que você poderá executar hoje (bem, quase). Vamos lá.
\n\nThreads Virtuais (Project Loom) — o fim da era dos bloqueios
\nEsta é, talvez, a mudança mais impactante. Threads virtuais (virtual threads) são threads leves que permitem escrever código assíncrono em estilo síncrono. Em 2025, elas finalmente se tornam estáveis e recomendadas para produção.
\nAntes, você escrevia algo como:
\nExecutorService executor = Executors.newFixedThreadPool(10);\nexecutor.submit(() -> {\n // chamada bloqueante\n String result = restTemplate.getForObject(url, String.class);\n return result;\n});\nO problema é que cada thread é um objeto pesado. 10 threads — ok, 10.000 — já dá problemas. Com threads virtuais, você pode executar milhões de threads sem se preocupar com memória:
\ntry (var executor = Executors.newVirtualThreadPerTaskExecutor()) {\n for (int i = 0; i < 100_000; i++) {\n executor.submit(() -> {\n // agora isso é leve\n String result = restTemplate.getForObject(url, String.class);\n return result;\n });\n }\n}\nA diferença chave: threads virtuais não estão vinculadas a threads do SO. Quando uma thread é bloqueada (por exemplo, em I/O), a JVM simplesmente a alterna para outra thread virtual. Sem custos adicionais.
\nO que isso significa para você? Esqueça frameworks assíncronos complexos como CompletableFuture e bibliotecas reativas. Em 2025, o código síncrono padrão com threads virtuais será mais rápido e mais simples.
\n\nComo migrar
\nSubstitua newFixedThreadPool por newVirtualThreadPerTaskExecutor. Se você usa Spring Boot — atualize para a versão 3.2+, que já tem suporte. Apenas certifique-se de que suas bibliotecas não usam synchronized em seções críticas — este é o único ponto onde as threads virtuais podem "travar".
Pattern Matching e Switch — o código fica mais curto
\nEm 2025, o pattern matching chegou ao switch e ao instanceof. Agora, em vez de um monte de if-else aninhados, você pode escrever de forma concisa e legível.
Era assim (estilo antigo):
\nif (obj instanceof String) {\n String s = (String) obj;\n System.out.println(s.length());\n} else if (obj instanceof Integer) {\n Integer i = (Integer) obj;\n System.out.println(i * 2);\n}\nAgora é assim (Java 21+):
\nswitch (obj) {\n case String s -> System.out.println(s.length());\n case Integer i -> System.out.println(i * 2);\n case null -> System.out.println("null");\n default -> System.out.println("unknown");\n}\nObserve o case null — é uma melhoria separada. Antes, o switch lançava NullPointerException ao receber null. Agora você pode tratar isso explicitamente.
Outro recurso interessante são os record patterns. Se você tem um record:
\nrecord Point(int x, int y) {}\nEntão pode escrever:
\nif (obj instanceof Point(int x, int y)) {\n System.out.println("x=" + x + ", y=" + y);\n}\nSem conversão de tipos desnecessária. Em 2025, isso será mainstream — use para deixar o código mais limpo.
\n\nColeta de Lixo: ZGC e Generational GC
\nEm 2025, a coleta de lixo ficou ainda mais rápida. O ZGC (Z Garbage Collector) agora está habilitado por padrão para a maioria das aplicações. Seu