Java is a statically typed, object-oriented language that compiles to bytecode and runs on the JVM. In 2026 it ships on a fixed six-month cadence, with a long-term support (LTS) release every two years. JDK 25, the current LTS, reached general availability on September 16, 2025. Java 26 followed on March 17, 2026, and Java 27 shipped on September 15, 2026. This guide covers what each release changed, how the code looks now, and which version to run in production.
Quick Takeaways
- Java 25 is the LTS to target. It is the first LTS since JDK 21 in 2023.
- Java 26 and 27 are short-term releases with six months of support each. Use them to preview what the next LTS (Java 29, September 2027) will contain.
- Boilerplate is shrinking. Compact source files, flexible constructor bodies, and module imports remove ceremony.
- The runtime is cheaper to run. Compact Object Headers cut per-object memory, and Virtual Threads handle massive concurrency without reactive code.
| Release | Type | GA Date | Headline Changes |
|---|---|---|---|
| Java 21 | LTS | Sept 2023 | Virtual Threads, record patterns, pattern-matching switch |
| Java 25 | LTS | Sept 16, 2025 | Scoped Values, compact source files, compact object headers |
| Java 26 | Non-LTS | Mar 17, 2026 | HTTP/3 client, AOT cache for any GC, G1 improvements |
| Java 27 | Non-LTS | Sept 15, 2026 | Compact object headers by default, post-quantum TLS, G1 as default everywhere |
What Is Java, Technically?
Java source files compile with javac into platform-neutral bytecode. The JVM loads that bytecode, verifies it, and runs it through an interpreter and the JIT compiler (C1 and C2). Hot methods get optimized to native code at runtime. Garbage collection manages memory, so you never call free().
Three properties explain why Java stays in enterprise stacks:
- Backward compatibility. A JAR built years ago usually runs unchanged on a current JDK.
- Predictable release train. Oracle and OpenJDK vendors publish on a fixed schedule, so planning is simple.
- Ecosystem depth. Spring Boot, Quarkus, Micronaut, Kafka, Elasticsearch, Gradle, and Maven are all JVM-native.
Java 25 LTS: What Changed and Why It Matters
Java 25 bundles years of preview work into production-ready features. Each one below includes a runnable example.
Compact Source Files and Instance Main Methods (JEP 512)
The main() method no longer needs to be static or public, and the args parameter is optional. A compact source file has no explicit class declaration.
// Hello.java
void main() {
IO.println("Hello, Java 25");
}
Run it with java Hello.java. The compiler wraps the method in an implicit class named after the file. IO.println() comes from the new java.lang.IO class. This is a real improvement for teaching and scripting.
Module Import Declarations (JEP 511)
One line pulls in every public type that a module exports.
import module java.base;
void main() {
// List, Map, and Stream resolve without individual imports
List<String> langs = List.of("Java", "Kotlin", "Scala");
Map<String, Integer> sizes = langs.stream()
.collect(Collectors.toMap(s -> s, String::length));
IO.println(sizes);
}
The output is a map of language names to their lengths, in unspecified order, because Map from toMap is a HashMap. Use this in scripts and small tools. Keep explicit imports in large codebases, where they document dependencies.
Flexible Constructor Bodies (JEP 513)
Before Java 25, super() had to be the first statement. You could not validate arguments before calling it without a static helper. Now you can.
class Amount {
protected final long cents;
Amount(long cents) { this.cents = cents; }
}
class PositiveAmount extends Amount {
PositiveAmount(long cents) {
// Validation runs BEFORE the superclass constructor
if (cents <= 0) {
throw new IllegalArgumentException("Amount must be positive: " + cents);
}
super(cents);
}
}
new PositiveAmount(-5) now fails fast, before any superclass state is initialized. The code in the prologue cannot reference this, which keeps initialization safe.
Scoped Values (JEP 506)
Scoped Values let a method share immutable data with its callees and with child threads, as a simpler alternative to thread-local variables. They pair naturally with Virtual Threads.
public class RequestHandler {
// One key per piece of context
private static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
void handle(String userId) {
// Binding exists only while run() executes
ScopedValue.where(USER_ID, userId).run(this::processOrder);
}
private void processOrder() {
// No parameter threading needed
IO.println("Processing order for " + USER_ID.get());
}
}
The binding is immutable and disappears when run() returns. There is no cleanup call and no leak risk when threads are pooled.
Compact Object Headers (JEP 519)
Enable it with -XX:+UseCompactObjectHeaders. Java 27 makes compact headers the default HotSpot layout, shrinking them from 96 bits to 64 bits on 64-bit architectures. Heaps full of small objects (DTOs, nodes, boxed values) shrink without code changes.
Java 26 and 27: The Road to the Next LTS
Java 26 and 27 matter because they show where Java 29 is heading.
Java 26:
- Adds HTTP/3 support to the standard HTTP client.
- Makes AOT object caching work with any garbage collector.
- Includes improvements to the G1 collector.
- Renames Stable Values to Lazy Constants, which better captures the high-level use case.
- Continues previews of Structured Concurrency and Primitive Types in Patterns, both close to finalization.
Java 27:
- Ships nine JEPs, with G1 as the default collector everywhere and compact object headers as the default layout.
- Delivers a post-quantum cryptography milestone, with a roadmap to bring comparable capabilities to LTS releases.
Looking ahead: JEP 401, Value Objects (Preview), is targeted for JDK 28, due March 2027. Project Valhalla is the largest runtime change planned for the platform. It lets you declare types that behave like int in memory but like classes in code.
Modern Java vs. Legacy Java: Refactoring Scenarios
The language you learn today is not the Java of 2014. These two comparisons show the difference.
Anti-Pattern 1: Manual Type Checks
Bad code:
// Fragile: add a new shape and nothing warns you
static double area(Object shape) {
if (shape instanceof Circle) {
Circle c = (Circle) shape;
return Math.PI * c.radius() * c.radius();
} else if (shape instanceof Rect) {
Rect r = (Rect) shape;
return r.w() * r.h();
}
throw new IllegalArgumentException("Unknown shape");
}
Good code:
// Sealed hierarchy + exhaustive switch: compiler enforces coverage
sealed interface Shape permits Circle, Rect {}
record Circle(double radius) implements Shape {}
record Rect(double w, double h) implements Shape {}
static double area(Shape shape) {
return switch (shape) {
case Circle(double r) -> Math.PI * r * r;
case Rect(double w, double h) -> w * h;
};
}
Add a Triangle to the permits clause and every incomplete switch fails to compile. That moves a runtime bug to build time.
Anti-Pattern 2: ThreadLocal for Request Context
Bad code:
private static final ThreadLocal<String> USER = new ThreadLocal<>();
void handle(String id) {
USER.set(id);
try {
process();
} finally {
USER.remove(); // Forget this and pooled threads leak data
}
}
Good code:
private static final ScopedValue<String> USER = ScopedValue.newInstance();
void handle(String id) {
ScopedValue.where(USER, id).run(this::process); // Auto-cleaned
}
| Feature | ThreadLocal | ScopedValue |
|---|---|---|
| Mutability | Mutable | Immutable |
| Lifetime | Until remove() |
Bounded by run() |
| Cleanup required | Yes | No |
| Virtual Thread cost | Per-thread copy | Low |
| Leak risk | High in pools | None |
Virtual Threads: Concurrency Without Reactive Code
Virtual Threads (finalized in Java 21) are lightweight threads scheduled by the JVM. A blocking call parks the virtual thread and frees the carrier thread. You write straightforward blocking code and still serve tens of thousands of concurrent requests.
import java.time.Duration;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class VirtualDemo {
public static void main(String[] args) {
// One virtual thread per task, closed automatically
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i ->
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1)); // Blocks cheaply
return i;
})
);
} // close() waits for all tasks
}
}
All 10,000 tasks finish in roughly one second. Platform threads would exhaust memory long before that. Use virtual threads for I/O-bound work. CPU-bound work still needs a bounded pool sized to your cores.
Java vs. Other Languages in 2026
| Criterion | Java | Kotlin | Go | Python |
|---|---|---|---|---|
| Execution model | JIT on JVM | JIT on JVM | Native AOT | Interpreted |
| Type safety | Static | Static, null-safe | Static | Dynamic |
| Concurrency | Virtual Threads | Coroutines | Goroutines | asyncio, GIL limits |
| Startup time | Moderate (AOT cache helps) | Moderate | Fast | Fast |
| Library ecosystem | Very large | Shares JVM’s | Growing | Very large |
| Typical use | Enterprise, backend | Android, backend | Cloud tooling | Data, scripting |
Java’s weak spot has been startup time. Project Leyden attacks that with AOT caching, and Java 26 widened it to every garbage collector.
Which Version Should You Run?
| Situation | Recommendation |
|---|---|
| New production service | Java 25 LTS |
| Currently on Java 21 | Plan a move to 25. The migration is usually low-risk. |
| Currently on Java 8 or 11 | Move to 25 in two steps, checking removed APIs and reflection warnings |
| Learning the language | Install the latest JDK, or 25 for stability |
| Testing upcoming features | Java 27 with --enable-preview |
Migration checklist:
- Run your test suite on JDK 25 with warnings visible.
- Update build plugins (Maven, Gradle) and bytecode libraries (ASM, ByteBuddy).
- Audit deep reflection on JDK internals and on
finalfields. Newer releases tighten both. - Benchmark with and without
-XX:+UseCompactObjectHeaders. - Consider replacing
ThreadLocalusage with ScopedValue where it is request-scoped.
Why Java Is Still Everywhere
Four factors keep Java embedded across banking, retail, telecom, and Android-adjacent backends:
- Stability. Code written to the specification keeps running across upgrades.
- Tooling. IntelliJ IDEA, JFR, JMX, and mature profilers make production debugging routine.
- Hiring. Millions of developers know the platform, which lowers staffing risk.
- Continuous modernization. Records, sealed types, pattern matching, virtual threads, and scoped values arrived without breaking existing code.
FAQ
Is Java still worth learning in 2026?
Yes. Java powers large backend systems, and recent releases removed much of the boilerplate that put beginners off. Compact source files let a first program fit in three lines.
What is the latest Java LTS version?
Java 25, released September 16, 2025, is the latest LTS. The next LTS is Java 29, expected in September 2027, following the two-year LTS cadence.
Is Java 26 an LTS release?
No. Java 26 is a short-term release with six months of support. The same applies to Java 27. Production teams usually stay on LTS and use non-LTS builds for evaluation.
Should I upgrade from Java 21 to Java 25?
For most teams, yes. Java 25 adds Scoped Values, Compact Object Headers, flexible constructor bodies, and module imports, and it carries a longer support window. Run your tests on JDK 25 first and benchmark memory use.




