Upgrade to Java 25
Java 25 is the current LTS (GA 2025-09-16, Oracle Premier Support to September 2030). For teams already on Java 17 or 21, the step is small and mostly additive — the disruptive packaging changes (modules, removed Java EE modules, strong encapsulation) were absorbed at 17. The hard work only resurfaces if you are still on Java 8/11.
Last verified · Updated May 22, 2026
Java 25 (LTS) reached general availability on 2025-09-16 and is the recommended target for new and modernizing projects; Oracle Premier Support runs through September 2030. From Java 17 or 21 the jump is small and additive — finalized features like scoped values (JEP 506), module import declarations (JEP 511), compact source files and instance main methods (JEP 512), flexible constructor bodies (JEP 513), and stream gatherers (JEP 485, final in JDK 24). The expensive breaking changes (the module system, removed javax.* Java EE modules, strong encapsulation of internals) only apply if you are still on Java 8/11.
Who should upgrade
- Apps on Java 21 wanting the next LTS with a fresh eight-year support window.
- Apps on Java 17 whose Oracle Premier Support ends September 2026 and who want a current, permissively licensed LTS.
- Teams on Java 8/11 that need a supported LTS runtime — budget for the 8 → 17 work first.
Who should wait
- Apps pinned to a critical dependency that lacks a Java 25-compatible release.
- Builds that rely on removed Java EE modules with no maintained replacement yet identified (Java 8/11 starters).
- Teams that would rather sit on Java 21 LTS until closer to its September 2028 Premier Support end.
What's new since Java 21 (JDK 22–25)
- Scoped values (JEP 506, final in 25) — a safer, immutable alternative to thread-locals, pairing well with virtual threads.
- Module import declarations (JEP 511, final in 25) — import all exported packages of a module with one 'import module' statement.
- Compact source files and instance main methods (JEP 512, final in 25) — smaller on-ramp programs without a class declaration or static main.
- Flexible constructor bodies (JEP 513, final in 25) — statements allowed before this()/super() so you can validate or prepare arguments.
- Stream gatherers (JEP 485, final in JDK 24) — custom intermediate stream operations via Stream.gather(Gatherer).
- Generational Shenandoah (JEP 521) and compact object headers (JEP 519, final in 25) reduce GC overhead and heap footprint.
- Still preview in 25: structured concurrency (JEP 505, 5th preview), primitive types in patterns/instanceof/switch (JEP 507, 3rd preview), stable values (JEP 502, preview). Don't depend on these in production without --enable-preview.
ℹ From 17/21 this is a toolchain bump, not a rewrite
Most projects on Java 17 or 21 bump the build toolchain to JDK 25, set the compiler release to 25, get a green build, then adopt new language and API features incrementally. The module-system and encapsulation work — the genuinely hard part — was already done at Java 17.
⚠ Structured concurrency is still a preview API in 25
Structured concurrency (StructuredTaskScope) remains a preview feature in JDK 25 (fifth preview) and its API has changed across previews — JDK 26 ships a sixth preview. Use scoped values (final in 25) freely, but gate any structured-concurrency code behind --enable-preview and expect API churn.
Java LTS support matrix
| Version | Released | LTS | Oracle Premier Support until | Support status |
|---|---|---|---|---|
| Java 8 | 2014-03-18 | Yes | March 2022 | Security-only |
| Java 11 | 2018-09-25 | Yes | September 2023 | Maintenance |
| Java 17 | 2021-09-14 | Yes | September 2026 | Active (Premier ending) |
| Java 21 | 2023-09-19 | Yes | September 2028 | Active |
| Java 25 | 2025-09-16 | Yes | September 2030 | Current LTS |
Recommended upgrade path
Fast path for apps already on Java 17 or 21
# 1. Point the build toolchain at JDK 25
export JAVA_HOME=$(/usr/libexec/java_home -v 25)
java -version
# 2. Maven: set the compiler release flag
./mvnw versions:set-property -Dproperty=maven.compiler.release -DnewVersion=25
# 3. Build + test on the new JDK
./mvnw clean verifyScoped values and module imports (both final in JDK 25)
// Java 25 (final): scoped values + module import declarations
import module java.base; // JEP 511: import all exported packages of java.base
static final ScopedValue<String> REQUEST_ID = ScopedValue.newInstance(); // JEP 506
void handle(String id) {
ScopedValue.where(REQUEST_ID, id).run(() -> process()); // immutable, virtual-thread friendly
}AI-assisted migration workflow
Upgrade this project to Java 25 (LTS). If the project is on Java 17 or 21, treat this as a toolchain bump: point the build toolchain at JDK 25, set the compiler release flag to 25, confirm Lombok/ASM/ByteBuddy are JDK 25-compatible, run the build and tests, then optionally adopt scoped values (JEP 506), module import declarations (JEP 511), and flexible constructor bodies (JEP 513). If the project is on Java 8/11, first do the 8 → 17 work (modules, removed javax.* Java EE modules, strong encapsulation) before stepping to 25. Do NOT introduce structured concurrency (JEP 505) or primitive patterns (JEP 507) unless --enable-preview is intentionally enabled — they are still preview in 25. Run the build and tests after each step and report before continuing.Safety: Incremental edits only. Keep preview features (structured concurrency, primitive patterns, stable values) out of production paths unless --enable-preview is deliberately set. Pause for review after the toolchain bump and after each dependency change.
PR review checklist
- Build toolchain and CI both target JDK 25
- maven.compiler.release / Gradle toolchain set to 25
- Lombok and other bytecode libraries bumped to JDK 25-compatible versions
- No accidental dependence on preview APIs (structured concurrency, primitive patterns, stable values) without --enable-preview
- If coming from Java 8/11: removed javax.* modules added as explicit dependencies and encapsulation flags scoped
- No InaccessibleObjectException or illegal-access warnings at startup
Rollback strategy
- Keep the toolchain bump, dependency bumps, and any feature adoption in separate commits.
- Revert JAVA_HOME and the compiler release flag to fall back to Java 21 or 17.
- Hold the upgrade behind a release branch until the full test suite is green on JDK 25.
Common errors
- Preview feature errors: 'X is a preview feature and is disabled by default' — remove the preview API or compile/run with --enable-preview --release 25.
- Lombok failing to compile — bump to a JDK 25-compatible release.
- From Java 8/11 only: NoClassDefFoundError: javax/xml/bind/* — add JAXB as an explicit dependency; InaccessibleObjectException — add a scoped --add-opens.
Related migration paths
- Migrate Google AutoValue to Java Records — Earlier step in the upgrade chain
- Java 17 to Java 21 Migration Guide — Related version path
- Java 17 to Java 25 Migration Guide — Upgrade path to same target
- Java 21 to Java 25 Migration Guide — Upgrade path to same target
- Java 8 to Java 17 Migration Guide — Related version path
- Upgrade to Java 21 — Related version path
Official sources
- JEPs integrated in JDK 22–25 (since the previous LTS, JDK 21) — openjdk.org (reliability 98%)
- JDK 25 Migration Guide — docs.oracle.com (reliability 98%)
- JDK 25 (Project) — GA 2025-09-16, LTS, feature/JEP list — openjdk.org (reliability 98%)
- Oracle Java SE Support Roadmap — oracle.com (reliability 98%)
Frequently asked questions
Is Java 25 or Java 26 the right target?
Target Java 25 (LTS). Java 26 (GA 2026-03-17) is a six-month non-LTS release — useful for trying new features, but it loses support when Java 27 ships in September 2026. Production teams should stay on the current LTS, which is Java 25 (Oracle Premier Support to September 2030).
How hard is Java 21 → 25?
Small. Both are post-module-system LTS releases, so it is mostly a toolchain bump plus optional feature adoption (scoped values, module imports, flexible constructors). There are no module-system or encapsulation surprises between 21 and 25.
Can I use structured concurrency in Java 25?
Only as a preview. Structured concurrency (JEP 505) is in its fifth preview in JDK 25 and the API is still evolving (a sixth preview ships in JDK 26). Scoped values (JEP 506), by contrast, are final in 25 and safe to use in production.