Java 25 shipped as an LTS release in September 2025. If you are already on Java 21 LTS, the jump is mostly smooth — most new features were previews in 21 that are now final — but there are a handful of deprecations, build-tool bumps, and behavioural tweaks that will trip you up if you do not prepare. This guide walks through the upgrade end-to-end with a migration checklist and AI prompts to automate the tedious parts.
Step 1 — Bump Your Build Files
The first thing to change is your compiler release target. For Maven, update all three properties to avoid split-brain between source, target, and bytecode version:
<!-- Maven (pom.xml) -->
<properties>
<maven.compiler.source>25</maven.compiler.source>
<maven.compiler.target>25</maven.compiler.target>
<maven.compiler.release>25</maven.compiler.release>
</properties>
// Gradle (build.gradle.kts)
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(25))
}
}
Use maven.compiler.release (not just source/target) — it sets the cross-compilation flag correctly and rejects internal APIs from older JDKs at compile time.
Step 2 — Upgrade Core Dependencies
Java 25 is stricter about annotation processor and bytecode enhancer compatibility. These are the minimum versions you need:
- Spring Boot 3.4+ — first version to officially support Java 25 across the whole starter ecosystem.
- Hibernate 7.x — drops the old CGLib-based bytecode enhancer; requires Jakarta EE 10 (jakarta.* packages, not javax.*). See Hibernate Tutorials for migration patterns.
- Lombok 1.18.34+ — earlier versions fail on Java 25’s updated annotation processor APIs. Check the Lombok changelog for any annotation changes.
- JUnit 5.11+ / JUnit 6 — JUnit 6 is the recommended runner; compatible with the virtual-thread executor.
- Mockito 5.12+ — required for virtual-thread-safe mocking and Java 25’s updated reflection model.
Step 3 — Remove Preview Flags
Structured concurrency (JEP 505) and scoped values (JEP 506) were preview in Java 21; they are final in Java 25. Delete --enable-preview from your Maven/Gradle compiler args and from any java launch scripts or Dockerfiles. Unnamed patterns and unnamed classes are also final in 25.
Step 4 — Watch for Breaking Changes
The following changes are the most likely to break existing code silently or loudly:
- Security Manager is gone. If your code calls
System.setSecurityManager(), it now throwsUnsupportedOperationExceptionat runtime. Replace with OS-level sandboxing or Java agents. - sun.misc.Unsafe memory methods deprecated for removal. Use
java.lang.foreign.MemorySegment(final since Java 22, JEP 454) instead. - Thread.stop / suspend / resume removed. These methods were deprecated for 20+ years and are now gone entirely.
- Finalization removed by default. Replace
Object.finalize()overrides with try-with-resources orjava.lang.ref.Cleaner. - JNI warnings on stderr by default. Audit any native libraries your project loads. The warning is not fatal but indicates upcoming stricter enforcement.
- Stricter strong encapsulation. Some internal JDK APIs accessible via
--add-opensin Java 21 may now require explicit module declarations.
Step 5 — Adopt the New Features
Upgrading without adopting new idioms is half the job. These are the highest-value migrations:
- ThreadLocal → ScopedValue — structured, immutable, and much cheaper on virtual threads.
- Pooled executors → virtual threads — swap I/O-bound
FixedThreadPoolfornewVirtualThreadPerTaskExecutor(). - instanceof chains → pattern-matching switch — exhaustive, null-safe, and works with records and sealed hierarchies.
- CompletableFuture.allOf → StructuredTaskScope — automatic cancellation, proper error propagation, and readable fan-out/fan-in.
Migration Checklist
- Update
maven.compiler.releaseor Gradle toolchain to 25. - Run
jdeps --jdk-internalson every module jar to find internal API usage. - Remove
--enable-previeweverywhere (compiler args, Dockerfiles, run scripts). - Upgrade Spring Boot, Hibernate, Lombok, JUnit, Mockito to minimum compatible versions.
- Replace all
SecurityManagerusage. - Replace finalizers with
Cleaneror AutoCloseable. - Run the full test suite; fix reflection-breakage and module-access errors.
- Switch I/O-bound executors to
newVirtualThreadPerTaskExecutor(). - Migrate
ThreadLocaltoScopedValuewhere thread scope is not needed. - Enable GraalVM CE / native-image builds if applicable; Java 25 ships improved native-image support.
- Measure startup time and GC pause times — Java 25 ships with improved ZGC generational mode.
AI Prompts You Can Use
Each prompt below is designed for a specific upgrade task. Paste it into Claude, ChatGPT, or Gemini and attach the relevant file(s):
Prompt 1 — Upgrade pom.xml
What it does: Rewrites your Maven build file to target Java 25 in a single AI-generated diff, bumping all the common framework versions simultaneously. Saves the tedious cross-referencing of compatibility matrices.
When to use it: At the very start of the upgrade — get the build green first, then deal with code changes.
Here is my pom.xml targeting Java 21. Update it to target Java 25. Bump Spring Boot to the latest 3.x, Hibernate to 7.x, Lombok to the latest, JUnit to 6.x, Mockito to 5.12+. List any dependency that does not have a Java 25 compatible version and suggest alternatives.
Prompt 2 — Find Breaking Changes
What it does: Scans a Java file for the specific API removals and deprecations that are most likely to break in Java 25: SecurityManager, Thread.stop/suspend/resume, Object.finalize, sun.misc.Unsafe memory methods, and old java.net.URL constructors. Produces a prioritized fix list.
When to use it: After bumping the compiler release — most breaks will surface here before you ever run the tests.
Scan this Java file for usages of SecurityManager, Thread.stop/suspend/resume, Object.finalize, sun.misc.Unsafe memory methods, and the old java.net.URL constructors. For each finding, provide a Java 25 replacement with a before/after code snippet.
Prompt 3 — Remove Preview Flags
What it does: Reviews a Maven or Gradle build file, removes all --enable-preview compiler and runtime arguments, and explains which features each flag was enabling — and whether those features are now final or were dropped entirely in Java 25.
When to use it: Once your dependencies are updated and the project compiles on Java 25, run this to clean up preview flags.
Review the attached Maven/Gradle build file and remove all --enable-preview compiler and runtime args. Explain which features they were enabling and whether any now need different handling in Java 25.
Prompt 4 — Modernize Idioms
What it does: Applies four Java 25 idioms to an existing file in one pass: records for data carriers, pattern-matching switch for type checks, sequenced collection APIs (getFirst/getLast), and ScopedValue instead of ThreadLocal. All changes preserve the original behaviour.
When to use it: After the project is green on Java 25 — this is the “nice to have” modernization pass that makes the codebase idiomatically Java 25.
Apply these Java 25 idioms to this file: records for data carriers, pattern-matching switch for type checks, sequenced collection APIs (getFirst/getLast), and ScopedValue instead of ThreadLocal. Preserve behaviour exactly and add a comment for each change explaining why the new form is preferred.
Prompt 5 — Virtual-Thread Safety Audit
What it does: Checks a class for the four patterns that cause problems when you flip to a virtual-thread executor: synchronized blocks around I/O (carrier pinning on Java 21–24), ThreadLocal usage, JNI calls, and code that implicitly assumes a bounded thread count. Returns a risk report with line numbers.
When to use it: Before switching any executor to newVirtualThreadPerTaskExecutor() — always audit first.
Before I flip my executors to virtual threads on Java 25, audit this class for: synchronized blocks around I/O (pinning), ThreadLocal usage, JNI calls, and any code that assumes a bounded thread pool. Report risks with line numbers and a severity (blocking/warning/low).
Prompt 6 — Upgrade Checklist Runner
What it does: Treats your entire project as input and produces a prioritised, risk-grouped upgrade checklist — blocking items (compile/test failures), warnings (deprecations and behavioural changes), and nice-to-haves (modernization opportunities) — with a rough developer-hours estimate for each group.
When to use it: At the beginning of a team upgrade to align on scope and schedule before writing a single line of code.
Act as a Java 25 upgrade reviewer. Given the attached project files, produce a prioritized checklist of upgrade tasks, group them by risk (blocking / warning / nice-to-have), and estimate effort in developer-hours per group.
See Also
- Java 21 to Java 25 LTS: Every Feature You Actually Need to Know
- Virtual Threads vs Platform Threads — Benchmarks and Code
- Java Streams API Deep Dive + Collectors Cookbook
- Java Concurrency Deep Dive: CompletableFuture, ExecutorService, ForkJoinPool
- Modern Java Testing: JUnit 6 + AssertJ + Mockito 5 + Testcontainers
- AI Prompts Playbook: Upgrading Java 8 → 11 → 17 → 21 → 25
- Demystifying Thread Safety in Java: A Practical Guide
- Java Memory Management and Garbage Collection: Deep Dive
- Hibernate Tutorials — Complete Library
- Overview of New Features in Java 19
FAQs
Can I skip Java 21 and go straight from Java 17 to Java 25?
Yes, but you will hit both the 17→21 and 21→25 changes simultaneously. The combined breakage surface is larger and harder to debug. Use the companion AI Prompts Playbook to work through each stage separately, even if you compile against Java 25 from the start.
Does IntelliJ IDEA support Java 25?
Yes — IntelliJ IDEA 2025.2 and later has full Java 25 language support, including pattern-matching switch, records, scoped values, and structured concurrency. Eclipse and VS Code (via the Language Server for Java) also have Java 25 support as of mid-2025.
Will Spring Boot 3.3.x work on Java 25?
It may compile and run for basic functionality, but Spring Boot 3.4 is the first version with official, tested Java 25 support. Running on an untested combination risks subtle issues with auto-configuration, Hibernate bytecode enhancement, and reflection-based injection. Upgrade Spring Boot first.
Conclusion
The Java 21 → 25 jump is the easiest LTS upgrade in years. Bump your build, remove preview flags, update Spring Boot / Hibernate / JUnit, and let the AI prompts above do the line-by-line modernization. The reward is final-form virtual threads, scoped values, structured concurrency, and a faster ZGC — all changes you would want to make anyway. The six-step checklist gives you a safe rollback point at each stage.