Artigos aprofundados sobre a tecnologia que molda o que vem a seguir.

Java Continua Melhorando e Ninguém Percebe

O ciclo de lançamentos de seis meses do Java mudou a linguagem em silêncio. Pattern matching, virtual threads e records a tornam irreconhecível.

Uma xícara de café brilhante enquanto um velho escritório sombrio se transforma em um espaço moderno e luminoso

Java é o Rodney Dangerfield das linguagens de programação: ninguém dá o devido valor. Mencione Java para a maioria dos desenvolvedores com menos de 30 anos e eles vão imaginar arquivos de configuração XML, enterprise beans e AbstractSingletonProxyFactoryBean. A reputação da linguagem ficou congelada em 2008, quando o Java 6 era a versão mais recente e escrever Java significava se afogar em boilerplate.

Mas algo notável aconteceu desde que o Java adotou um ciclo de lançamentos de seis meses em 2017. A linguagem vem evoluindo em um ritmo que seria impensável nos intervalos de quatro anos entre Java 6, 7 e 8. Records. Sealed classes. Pattern matching. Virtual threads. String templates. Structured concurrency. Cada versão traz recursos que tornam o código Java mais curto, mais expressivo e mais agradável de escrever. O Java 26 mantém essa sequência, e a distância entre “o Java como ele é” e “o Java como as pessoas imaginam” nunca foi tão grande.

Os Recursos que Mudaram Tudo

Se você não escreve Java desde o Java 8, está perdendo uma década de melhorias. Veja como a linguagem está hoje.

Records (Java 14) são classes de dados imutáveis. O que antes exigia 50 linhas de boilerplate — campos, construtor, getters, equals, hashCode, toString — agora cabe em uma linha.

// Java 8: 50 lines of boilerplate
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Point)) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
@Override
public int hashCode() { return Objects.hash(x, y); }
@Override
public String toString() { return "Point[x=" + x + ", y=" + y + "]"; }
}
// Java 14+: one line
record Point(int x, int y) {}

Pattern matching (Java 16-21, ainda em evolução) permite desestruturar objetos em expressões switch e em verificações instanceof. Isso elimina as cadeias em cascata de if-else-instanceof que atormentavam o código Java.

// Pattern matching with sealed types and records
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}
// Exhaustive pattern matching — compiler verifies all cases covered
double area(Shape shape) {
return switch (shape) {
case Circle(var r) -> Math.PI * r * r;
case Rectangle(var w, var h) -> w * h;
case Triangle(var b, var h) -> 0.5 * b * h;
};
}

Virtual threads (Java 21) são threads leves gerenciadas pela JVM em vez do sistema operacional. Você pode criar milhões delas sem esgotar a memória ou os handles de threads do SO. Isso torna o modelo “uma thread por requisição” — o modelo de concorrência mais simples — viável em escala.

// Create a million concurrent tasks — try this with OS threads
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 1_000_000).forEach(i -> {
executor.submit(() -> {
// Each task gets its own virtual thread
// Blocking I/O (HTTP, DB, file) automatically yields
var response = httpClient.send(request, BodyHandlers.ofString());
return response.body();
});
});
}
// All done — executor auto-closes, waits for completion

Virtual threads são a resposta do Java às goroutines do Go e às coroutines do Kotlin. A diferença: você não precisa de sintaxe especial nem de “colorir” suas funções. Código bloqueante comum funciona — a JVM suspende e retoma as virtual threads de forma transparente nos pontos de bloqueio. Seu cliente HTTP bloqueante, seu driver JDBC e seu código de I/O de arquivos se beneficiam sem nenhuma modificação.

Por que Ninguém Percebe

A trajetória de melhorias do Java é realmente impressionante. Então por que a percepção ainda está presa em 2008?

Primeiro, a inércia corporativa. Muitas organizações rodam Java 8 ou 11 porque atualizar uma base de código grande é caro e arriscado. Quando os desenvolvedores falam em “Java”, estão falando da versão que a empresa usa, não da release mais recente. Desenvolvedores do Java 8 não experimentaram records, pattern matching ou virtual threads.

Segundo, a camada de frameworks. Spring Boot, Jakarta EE e outros frameworks corporativos adicionam sua própria complexidade por cima da linguagem. Os desenvolvedores culpam o “Java” pela cerimônia de anotações do Spring, mas isso é do framework, não da linguagem. O Java moderno sem um framework pesado é surpreendentemente enxuto.

Terceiro, o momentum cultural. A cultura de tecnologia atribui linguagens a tribos. Java é a linguagem “corporativa”, associada a grandes empresas, processos burocráticos e over-engineering. Essa reputação foi merecida — o desenvolvimento em Java por volta de 2005 mereceu as críticas. Mas reputações sobrevivem às suas causas por anos.

A Vantagem da JVM

As melhorias na linguagem importam, mas a maior vantagem do Java continua sendo a JVM. Depois de três décadas de otimização, a HotSpot JVM é um dos ambientes de execução mais sofisticados já construídos.

  • Compilação JIT. A JVM analisa o código em execução e compila os métodos mais quentes para código nativo otimizado. Em aplicações de longa duração (servidores, processamento de dados), o desempenho da JVM muitas vezes iguala ou supera o do C++, porque o JIT pode otimizar para o comportamento real em runtime, e não apenas para uma análise estática.
  • Coleta de lixo. Implementações modernas de GC (ZGC, Shenandoah) oferecem pausas abaixo de um milissegundo, mesmo com heaps de terabytes. A crítica de que “pausas do GC matam a latência” deixou de ser verdade há anos — esses coletores rodam concorrentemente com a aplicação.
  • Observabilidade. O JFR (Java Flight Recorder) oferece profiling seguro para produção com overhead próximo de zero. Você pode perfilar uso de CPU, alocação de memória, contenção de locks, latência de I/O e comportamento do GC sem desacelerar a aplicação.
  • Maturidade do ecossistema. O Maven Central tem milhões de pacotes. As ferramentas de build (Gradle, Maven) são bem conhecidas. O ecossistema de testes (JUnit, Mockito, Testcontainers) é excelente. A história de deploy (containers, imagens nativas com GraalVM) amadureceu significativamente.

GraalVM e Imagens Nativas

O maior ponto fraco do Java sempre foi o tempo de inicialização. A JVM leva centenas de milissegundos para inicializar, carregar classes e aquecer o compilador JIT. Para funções serverless e ferramentas de linha de comando, esse custo é inaceitável.

O GraalVM Native Image resolve isso compilando aplicações Java antecipadamente (ahead-of-time) para executáveis independentes. Não é preciso JVM. O tempo de inicialização cai de centenas de milissegundos para um único dígito em milissegundos. O uso de memória também diminui, porque não há interpretador, compilador JIT nem metadados de classes para carregar.

O trade-off: imagens nativas perdem a otimização adaptativa da JVM. Uma aplicação compilada pelo JIT fica mais rápida com o tempo, à medida que a JVM aprende os caminhos quentes. Uma imagem nativa começa rápida, mas não melhora. Para processos de curta duração (ferramentas CLI, serverless), as imagens nativas vencem. Para servidores de longa duração, o JIT da JVM acaba produzindo código mais rápido. Escolha a ferramenta certa para as características do seu runtime.

Java Moderno vs. as Alternativas

As linguagens que “substituiriam” o Java — Go, Kotlin, Rust — são excelentes. Mas o Java moderno compete de forma mais eficaz do que as pessoas imaginam.

A simplicidade do Go é atraente, mas o Java agora tem virtual threads (equivalentes às goroutines) e um sistema de tipos muito mais rico. Sistemas de tipos importam em bases de código grandes, e o do Java — especialmente com sealed types e pattern matching — pega erros que o sistema de interfaces do Go deixa passar.

O Kotlin melhora a sintaxe do Java, mas roda na mesma JVM. A maioria das vantagens do Kotlin sobre o Java 8 (null safety, data classes, coroutines) tem equivalentes parciais ou completos no Java moderno (padrões com Optional, records, virtual threads). O Kotlin continua mais conciso, mas a diferença diminuiu bastante.

O Rust ocupa um nicho diferente — programação de sistemas sem coletor de lixo. O Java não deveria competir ali. Mas, para aplicações server-side, processamento de dados e software corporativo, a combinação de desempenho, segurança e maturidade do ecossistema do Java é difícil de bater.

A Inovação Real é o Ciclo de Seis Meses

Mais do que qualquer recurso isolado, o ciclo de lançamentos de seis meses mudou a trajetória do Java. Antes de 2017, as releases do Java eram eventos de três a quatro anos que tentavam entregar tudo de uma vez. Recursos atrasavam porque não estavam prontos, o que atrasava a release inteira, o que criava pressão para empurrar tudo para a próxima versão, que também atrasava. O Java 9 levou mais de três anos em parte por causa da complexidade do sistema de módulos.

Com releases de seis meses, os recursos são entregues quando ficam prontos. O pattern matching foi entregue de forma incremental entre o Java 16 e o 24, com cada versão adicionando uma peça. As virtual threads passaram por duas versões de preview antes da entrega final. Essa abordagem incremental permite que a equipe do Java reúna feedback do mundo real e ajuste os designs antes de se comprometer com decisões de API permanentes.

A lição vale além do Java: releases pequenas e frequentes se acumulam e geram mudanças transformadoras. Cada release do Java desde a 17 foi modesta. Somadas, reinventaram a linguagem. Se você experimentou Java há cinco anos e desistiu por causa da verbosidade, experimente de novo. Você pode se surpreender com o que vai encontrar.