Modern Java (9–21)
Java has evolved dramatically since the landmark Java 8 release. From a new module system that reshapes how you package applications, to lightweight virtual threads that can handle millions of concurrent tasks, the versions from Java 9 through Java 21 transformed Java into a genuinely modern language — without losing the reliability and performance that made it so widely adopted.
Why “Modern Java” Matters
For years, Java had a reputation for being verbose and slow to evolve. That changed with a new six-month release cadence introduced in 2017. Since then, Java ships a new version every March and September, with Long-Term Support (LTS) releases at regular intervals.
Here is a quick summary of the LTS releases you will care about most:
| Version | Release | LTS? | Headline Features |
|---|---|---|---|
| Java 9 | Sep 2017 | No | Module System (JPMS), JShell |
| Java 11 | Sep 2018 | Yes | var for lambdas, new String/File APIs, HTTP Client |
| Java 17 | Sep 2021 | Yes | Sealed classes, pattern matching for instanceof, records |
| Java 21 | Sep 2023 | Yes | Virtual threads, pattern matching for switch, sequenced collections |
Tip: If you are starting a new project today, target Java 21. It is the current LTS and brings the most modern feature set with production-grade support.
The Release Cadence — How Java Evolves Now
Before Java 9, new major versions came every few years and bundled enormous, risky changes. The new model is different: small, focused features land in each six-month release, guided by the JDK Enhancement Proposal (JEP) process. Features go through preview (experimental, off by default), followed by incubator (for APIs), and finally standard (fully supported) stages.
This means you will see phrases like “preview in Java 16, standard in Java 17.” Enabling a preview feature requires the --enable-preview flag at both compile and run time — use previews in experiments, not production code.
A Tour of the Big Ideas
Modern Java is not a random list of features. Several themes run through the releases:
1. Better expressiveness, less boilerplate Records give you immutable data carriers in a single line. Text blocks let you write multi-line strings cleanly. var reduces redundant type declarations. Switch expressions turn a statement into an expression with no fall-through bugs.
// Before modern Java — a simple data class required lots of boilerplate
public final class Point {
private final int x;
private final int y;
public Point(int x, int y) { this.x = x; this.y = y; }
public int x() { return x; }
public int y() { return y; }
@Override public boolean equals(Object o) { /* ... */ return false; }
@Override public int hashCode() { /* ... */ return 0; }
@Override public String toString() { return "Point[x=" + x + ", y=" + y + "]"; }
}
// With records (Java 16+) — same semantics, one line
record Point(int x, int y) {}
2. Safer, more expressive type systems Sealed classes let you declare exactly which subclasses exist, enabling exhaustive pattern matching. Pattern matching eliminates manual casts and verbose instanceof chains. Together, they bring algebraic-data-type style programming to Java.
// Pattern matching for instanceof (Java 16+)
Object obj = "Hello, Java 17!";
if (obj instanceof String s && s.length() > 5) {
System.out.println(s.toUpperCase());
}
Output:
HELLO, JAVA 17!
3. Scalable concurrency
Virtual threads (Java 21) are the biggest concurrency story since Java 5’s java.util.concurrent. They let you write simple, blocking code that scales to millions of concurrent operations — no reactive plumbing required.
// Java 21 — spin up 10,000 virtual threads trivially
try (var executor = java.util.concurrent.Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10_000; i++) {
executor.submit(() -> {
Thread.sleep(100); // blocking is fine on a virtual thread
return null;
});
}
} // executor.close() awaits all tasks
4. Better encapsulation and modularity
The Java Platform Module System (JPMS) introduced in Java 9 lets you declare explicit module boundaries, hide internal packages, and ship minimal custom JREs with jlink.
Under the Hood
Every Java release ships as a new class file version. Java 21 class files use version 65. The JVM is backward-compatible, so a Java 21 JVM can run classes compiled for older versions — but not vice versa.
Preview features are compiled with a special flag set in the class file that the standard JVM rejects unless --enable-preview is also provided at runtime. This prevents accidental deployment of unstable APIs.
JIT compilation improves continuously across versions. Java 17’s JVM ships with a faster G1 GC that uses region-pinning by default. Java 21 introduces generational ZGC, reducing pause times even further. You get better performance on the same code simply by upgrading — which is a strong argument for staying on a current LTS.
Note: The JVM Architecture page has a deep dive into how the JVM executes bytecode. Understanding it helps you make sense of why virtual threads are so efficient — they are scheduled by the JVM, not the OS.
Migrating From Java 8
Many teams are still running Java 8. Migrating to Java 11 or 21 is very achievable, but there are a few common friction points:
- Removed APIs:
javax.xml.bind(JAXB),sun.misc.Unsafeinternals, and some deprecated APIs were removed. Add them back as Maven/Gradle dependencies if you need them. - Strong encapsulation: JPMS now denies reflective access to JDK internals by default. You may need
--add-opensflags while migrating. varis a context keyword, not a reserved word, so code likeint var = 5;still compiles in older source levels (though you probably should not write that).
Warning: Upgrading your Java version is usually safe, but always run your full test suite. Libraries that relied on internal JDK APIs may break on Java 17+.
Choosing the Right Java Version
For new projects: use Java 21 LTS.
For existing projects: migrate to at least Java 11 LTS for the improved APIs, or Java 17 LTS for sealed classes and records.
For experimentation: any current release is fine — preview features are fun to explore.
If you are new to Java, start with the introduction and Hello World before diving into modern features. If you want to see what Java looked like before these improvements, check out the Java 8 Features page.
In This Section
- Java 9: Modules (JPMS) — Understand the module system that restructured the entire JDK and enables you to create lean, secure applications with explicit dependencies.
- Java 10: var — Learn how local variable type inference reduces boilerplate without sacrificing type safety.
- Records — Create concise, immutable data classes with automatic constructors, accessors,
equals,hashCode, andtoString. - Sealed Classes — Declare a closed hierarchy of types to enable exhaustive pattern matching and safer domain modeling.
- Text Blocks — Write clean multi-line strings (JSON, SQL, HTML) without escape characters and awkward concatenation.
- Switch Expressions — Upgrade the classic
switchstatement into an expression with arrow syntax, no fall-through, and a return value. - Pattern Matching — Use
instanceofpatterns andswitchpatterns to eliminate manual casts and write expressive type-dispatch code. - Virtual Threads (Project Loom) — Run millions of concurrent lightweight threads without the overhead of OS threads, making blocking I/O code scale like async code.
- Java 11 LTS Features — A curated tour of the most useful additions in the Java 11 LTS release, including new String methods, the HTTP Client, and
varin lambdas. - Java 17 LTS Features — Explore the stable features that landed in Java 17, from sealed classes and records to improved pseudo-random generators and stronger encapsulation.
- Java 21 LTS Features — Discover Java 21’s headline features: virtual threads, sequenced collections, pattern matching for switch, and record patterns.
Related Topics
- Java 8 Features — The landmark release that introduced lambdas, streams, and the Date/Time API — the foundation modern Java builds upon.
- Lambda Expressions — Lambdas from Java 8 are the cornerstone of functional-style Java, and work hand-in-hand with modern stream and switch patterns.
- Stream API — Introduced in Java 8 and improved in every subsequent release, streams remain central to modern data processing in Java.
- JVM Architecture — Understanding the JVM helps you reason about virtual threads, GC improvements, and why upgrading Java versions often gives you free performance gains.
- Garbage Collection Deep-Dive — Modern Java GCs (G1, ZGC, Shenandoah) have improved significantly across versions 9–21.
- Virtual Threads (Project Loom) — The single most transformative feature in Java 21, reshaping how you write concurrent applications.