O Java 25 chega com uma forte mistura de qualidade de vida da linguagem, melhor inicialização, criação de perfil poderosa e APIs práticas.
Este post é um tour prático: seções curtas, código executável e cenários concretos onde você usará cada recurso em breve. Cada trecho é intencionalmente minimalista para que você possa copiar, adaptar e executar rapidamente.
- 🚀 Destaques do Java 25
- Atualizações de linguagem e sintaxe
- Programas compactos e importações mais limpas
- Concorrência e contexto
- Blocos de construção de segurança
- Criação de perfil e observabilidade
- Inicialização e aquecimento
- Notas
- Referências
🚀 Destaques do Java 25
- ✅ Corpos de construção flexíveis (JEP 513)
- 🧩 Padrões primitivos em
instanceof
eswitch
(JEP 507) - 📦 Programas compactos e importação de módulos (JEP 512 + JEP 511)
- 🔄 Concorrência estruturada + valores com escopo (JEP 505 + JEP 506)
- 🔐 API KDF e codificação PEM (JEP 510 + JEP 470)
- 📊 Novas ferramentas de perfil JFR (JEP 520 + JEP 509)
- ⚡ Inicialização mais rápida via cache AOT (JEP 514 + JEP 515)
Atualizações de linguagem e sintaxe
JEP 513: Corpos de construção flexíveis (✅ final)
👉 PEC 513
O que é: Você pode executar instruções antes de chamar super(...)
ou this(...)
. Um novo construtor “prólogo” permite validar entradas e inicializar seus próprios campos com segurança antes de delegar na hierarquia.
Quando você vai usar: Validação à prova de falhas e estabelecimento da integridade da subclasse antes que uma superclasse possa chamar métodos substituíveis.
Antes (abordagem anterior: tinha que chamar super
primeiro, muitas vezes enviando a validação para uma fábrica ou fazendo isso depois super
, arriscando um estado parcialmente inicializado se a superclasse usasse métodos substituíveis):
// BEFORE (pre-Java 25)
class Person {
Person(String name) { init(); }
void init() { /* might call overridable methods */ }
}
class Employee extends Person {
private String payrollId; // cannot set before super
Employee(String name, String payrollId) {
super(name); // must be first line
if (payrollId == null || !payrollId.matches("EMP-\\d{4}")) {
throw new IllegalArgumentException("Invalid payrollId");
}
this.payrollId = payrollId; // set only after super
}
}
Limitações antes:
- A validação é executada somente após
super
, então o código da superclasse pode observar um estado de subclasse incompleto. - Soluções alternativas: fábricas estáticas, inicialização em duas fases ou validação duplicada anterior.
Depois (ID da folha de pagamento do funcionário validado e inicializado antes do construtor da Pessoa):
// src/main/java/com/loiane/people/Employee.java
package com.loiane.people;
class Person {
final String name;
Person(String name) { this.name = name; log("constructed: " + name); }
void log(String msg) { System.out.println(msg); }
}
class Employee extends Person {
final String payrollId;
Employee(String name, String payrollId) {
// Prologue: validate & set subclass state before calling super
if (payrollId == null || !payrollId.matches("EMP-\\d{4}"))
throw new IllegalArgumentException("Invalid payrollId");
this.payrollId = payrollId; // allowed before super
super(name); // delegation after establishing invariants
log("ready: " + this.payrollId);
}
public static void main(String[] args) {
new Employee("Ana", "EMP-1234");
}
}
O que está acontecendo aqui?
- Validação + configuração invariante acontecem no prólogo (antes
super
). - A atribuição de campo
payrollId
é permitida com antecedência. - O estado da subclasse existe antes da execução da lógica da superclasse.
JEP 507: Padrões primitivos em instanceof e switch (🧪 terceira prévia)
👉 PEC 507
O que é: A correspondência de padrões agora lida com tipos primitivos em instanceof
e switch
. Você pode testar e vincular valores restritos com segurança, sem verificações manuais de intervalo; switch
suporta boolean
, long
, float
e double
também.
Quando você usará: Conversões numéricas mais seguras, análise expressiva e regras de negócios mais limpas.
Antes (fundição manual + ramificações separadas: mais texto padronizado e risco de alargamento acidental):
// BEFORE (pre-Java 25)
public class TierLegacy {
static String tierFor(Object usage) {
if (usage instanceof Integer) {
int i = (Integer) usage;
if (i < 1) return "FREE";
if (i < 1_000) return "STARTER";
if (i < 10_000) return "PRO";
return "ENTERPRISE";
} else if (usage instanceof Long) {
long l = (Long) usage;
if (l < 10_000) return "PRO"; else return "ENTERPRISE";
} else if (usage instanceof Double) {
double d = (Double) usage;
int asInt = (int) d; // silent truncation risk
if (asInt == d) {
return tierFor(asInt); // re-box to Integer call
}
return d < 1_000.0 ? "STARTER" : "PRO";
}
return "UNKNOWN";
}
}
Limitações antes:
- Repetição de conversões e declarações de variáveis.
- Possível truncamento silencioso ao restringir
double
paraint
. - Nenhum padrão de proteção em linha com
switch
a expressão.
Depois (níveis de preços com estreitamento preciso e proteções):
// src/main/java/com/loiane/billing/Tier.java
package com.loiane.billing;
public class Tier {
static String tierFor(Object usage) {
if (usage instanceof int i) {
return switch (i) {
case int v when v < 1 -> "FREE";
case int v when v < 1_000 -> "STARTER";
case int v when v < 10_000 -> "PRO";
case int v -> "ENTERPRISE";
};
}
if (usage instanceof long l) {
return switch (l) {
case long v when v < 10_000 -> "PRO";
case long v -> "ENTERPRISE";
};
}
if (usage instanceof double d) {
// Bind only when d converts exactly to int (no silent truncation)
if (d instanceof int exact) {
return tierFor(exact);
}
return d < 1_000.0 ? "STARTER" : "PRO";
}
return "UNKNOWN";
}
public static void main(String[] args) {
System.out.println(tierFor(0)); // FREE
System.out.println(tierFor(999)); // STARTER
System.out.println(tierFor(9_999)); // PRO
System.out.println(tierFor(10_000L)); // ENTERPRISE (long path)
System.out.println(tierFor(42.0)); // STARTER (double exact -> int)
}
}
O que está acontecendo aqui?
instanceof int exact
vincula-se apenas à conversão exata (sem perda silenciosa).switch
sobrelong
e guardas de valores deixam clara a intenção.
Como executar (prévia):
javac --enable-preview --release 25 -d out src/main/java/com/loiane/billing/Tier.java
java --enable-preview -cp out com.loiane.billing.Tier
Programas compactos e importações mais limpas
JEP 512: Arquivos de origem compactos + instância principal (✅ final)
👉 PEC 512
O que é: Você pode escrever um arquivo apenas com métodos/campos; o compilador o encapsula em uma classe implícita. void main()
Pode ser um método de instância. O novo java.lang.IO
facilita a E/S do console.
Quando você usará: Katas, scripts, ensinamentos, demonstrações rápidas.
Antes (classe explícita + main estático necessário):
// BEFORE (pre-Java 25)
public class HelloClassic {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Depois (fonte compacta simplificada Hello World):
// Name this file: Hello.java (compact source)
void main() {
IO.println("Hello, World!");
}
O que está acontecendo aqui?
- Nenhuma declaração de classe;
void main()
é o ponto de entrada. IO.println
(java.lang.IO) evita clichês.
Run:
java Hello.java
JEP 511: Declarações de importação de módulos (✅ final)
👉 PEC 511
O que é: import module <name>;
importa todos os pacotes exportados de um módulo (e transitivos), até mesmo do código classpath.
Quando você vai usar: Prototipagem e aprendizado; extração rápida de APIs amplas (por exemplo, java.se
em código modular).
Código (análise HTTP + JSON com menos importações):
// src/main/java/com/loiane/http/FetchTodo.java
package com.loiane.http;
import module java.base;
import module java.net.http;
public class FetchTodo {
public static void main(String[] args) throws Exception {
var client = java.net.http.HttpClient.newHttpClient();
var req = java.net.http.HttpRequest.newBuilder(URI.create("https://jsonplaceholder.typicode.com/todos/1")).build();
var res = client.send(req, java.net.http.HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
}
}
O que está acontecendo aqui?
- Duas importações de módulos trazem todos
java.net.http
os tipos necessários; ainda qualificamos os tipos para maior clareza. - Caso surja alguma ambiguidade, adicione uma importação específica para resolvê-la.
Concorrência e contexto
JEP 506 + JEP 505: Valores com escopo (✅ final) + Concorrência estruturada (🧪 quinta prévia)
O que é: Valores com escopo são uma alternativa mais segura e herdável a ThreadLocal
. A simultaneidade estruturada fornece um ciclo de vida para grupos de tarefas.
Quando você usará: Propagação de IDs de solicitação, autenticação ou localidade em threads virtuais com cancelamento claro.
Código (correlação-id em um grupo de tarefas):
/ src/main/java/com/loiane/observability/Trace.java
package com.loiane.observability;
import java.lang.ScopedValue;
import java.util.concurrent.StructuredTaskScope; // preview
public class Trace {
static final ScopedValue<String> CORR_ID = ScopedValue.newInstance();
static void log(String msg) { System.out.println("[" + CORR_ID.orElse("-") + "] " + msg); }
public static void main(String[] args) throws Exception {
var id = java.util.UUID.randomUUID().toString();
ScopedValue.where(CORR_ID, id).run(() -> {
try (var scope = new StructuredTaskScope<Void>()) { // simplified example
var a = scope.fork(() -> { log("load customer"); return null; });
var b = scope.fork(() -> { log("load orders"); return null; });
scope.join();
log("done");
}
});
}
}
O que está acontecendo aqui?
ScopedValue.where(CORR_ID, id).run(...)
vincula o ID de correlação para todo o escopo da tarefa.- Threads virtuais (se usadas) herdam o valor do escopo sem vazamentos.
Executar (visualização para simultaneidade estruturada):
javac --enable-preview --release 25 -d out src/main/java/com/loiane/observability/Trace.java
java --enable-preview -cp out com.loiane.observability.Trace
Blocos de construção de segurança
JEP 510: API KDF (✅ final) com HKDF
👉 PEC 510
O que é: Padrão javax.crypto.KDF
para derivar chaves; inclui HKDF e especificações de parâmetros.
Quando você vai usar: Chaves de sessão, fluxos HPKE/KEM, chaves de semente.
Código (derivar material de chave AES para uma exportação):
// src/main/java/com/loiane/crypto/HkdfDemo.java
package com.loiane.crypto;
import javax.crypto.*;
import javax.crypto.spec.HKDFParameterSpec;
import java.security.spec.AlgorithmParameterSpec;
public class HkdfDemo {
public static void main(String[] args) throws Exception {
byte[] ikm = "seed-key-material".getBytes();
byte[] salt = "salt".getBytes();
byte[] info = "export-label".getBytes();
KDF hkdf = KDF.getInstance("HKDF-SHA256");
AlgorithmParameterSpec params = HKDFParameterSpec
.ofExtract().addIKM(ikm).addSalt(salt)
.thenExpand(info, 32);
SecretKey key = hkdf.deriveKey("AES", params);
System.out.println("Key algorithm: " + key.getAlgorithm());
System.out.println("Key length: " + key.getEncoded().length);
}
}
JEP 470: codificações PEM (🧪 visualização)
👉 PEC 470
O que é: APIs de primeira classe para ler/escrever PEM e DER com criptografia opcional.
Quando você vai usá-lo: Importar/exportar chaves sem bibliotecas de terceiros; manipular envelopes PEM de forma limpa.
Código (escreva um registro PEM simples de bytes):
// src/main/java/com/loiane/crypto/PemWrite.java
package com.loiane.crypto;
import java.nio.file.*;
import java.util.HexFormat;
import jdk.security.pem.*; // preview API
public class PemWrite {
public static void main(String[] args) throws Exception {
byte[] payload = HexFormat.of().parseHex("DEADBEEF");
var rec = PEMEncoder.encode("TEST-DATA", payload);
Files.writeString(Path.of("test-data.pem"), rec);
}
}
Executar (prévia):
javac --enable-preview --release 25 -d out src/main/java/com/loiane/crypto/*.java
java --enable-preview -cp out com.loiane.crypto.PemWrite
Criação de perfil e observabilidade
JEP 520: Tempo e rastreamento do método JFR (✅ final)
👉 PEC 520
O que é: Métodos específicos de tempo e rastreamento por instrumentação de bytecode: sem necessidade de alterações no código. Configuração via CLI, arquivos de configuração ou JMX.
Quando você vai usar: Identifique inicializadores lentos, pontos críticos em estruturas ou vazamentos em ciclos de vida de recursos.
Experimente (rastreie HashMap::resize
enquanto executa seu aplicativo):
java '-XX:StartFlightRecording:jdk.MethodTrace#filter=java.util.HashMap::resize,filename=recording.jfr' -jar yourapp.jar
jfr print --events jdk.MethodTrace --stack-depth 20 recording.jfr
O JMX programático (rastreamento remoto sob demanda) também é suportado.
JEP 509: Criação de perfil de tempo de CPU JFR (experimental, Linux)
👉 PEC 509
O que é: Perfis precisos de tempo de CPU usando o temporizador de CPU do Linux; atribui tempo nativo aos quadros Java.
Quando você vai usar: Criação de perfil de produção com baixa sobrecarga; priorize o trabalho vinculado à CPU.
Início rápido:
java -XX:StartFlightRecording=jdk.CPUTimeSample#enabled=true,filename=cpu.jfr -jar yourapp.jar
jfr view cpu-time-hot-methods cpu.jfr
Inicialização e aquecimento
JEP 514: Criação de cache AOT em uma etapa (✅ final)
👉 PEC 514
O que é: -XX:AOTCacheOutput=app.aot
executa uma invocação de treinamento e cria o cache em uma única etapa. Uso -XX:AOTCache=app.aot
em produção.
Cenário: A etapa de CI treina a inicialização (rota HTTP típica + desserialização) e publica o artefato de cache. Os pods de produção são montados app.aot
para inicializações a frio consistentemente rápidas.
# Train and create cache (one-shot)
java -XX:AOTCacheOutput=app.aot -jar yourapp.jar --train
# Use cache in prod
java -XX:AOTCache=app.aot -jar yourapp.jar
JEP 515: Criação de perfil de método antecipado (✅ final)
👉 PEC 515
O que é: Os perfis de treinamento são armazenados no cache AOT para que o JIT seja otimizado mais cedo na inicialização.
Dica: Para infraestrutura dividida, defina opções somente de criação JDK_AOT_VM_OPTIONS
se o treinamento e a criação ocorrerem em instâncias diferentes.
Notas
- Os recursos de linguagem de visualização (padrões primitivos, simultaneidade estruturada) são necessários
--enable-preview
para compilar e executar. - As adições do JFR permitem tanto amostragem (tempo de CPU) quanto tempo/rastreamento precisos; combine com o JMC para análise profunda.
- Os fluxos de trabalho AOT de Leyden (AOTCacheOutput + criação de perfil de método) melhoram as inicializações a frio e reduzem o tempo de aquecimento.
- Fique de olho nos JEPs de pré-visualização/incubadora; os detalhes (nomes de métodos, formatos de API secundários) ainda podem mudar um pouco até a versão final.
Use os recursos que se adequam ao seu caso de uso hoje e mantenha as visualizações no seu radar: elas já são úteis e estão quase prontas.
Referências
Boa codificação!