Why Java's Version History is Practical Knowledge, Not Trivia
Every Java feature was introduced in a specific version, and assuming otherwise is how legacy codebases get broken. Reading an older codebase, planning an upgrade, or copying a modern snippet into an existing project all require knowing exactly which version introduced what โ not as historical curiosity, but as a precondition for the code actually compiling.
// BEFORE โ assuming a modern feature is universally available
var order = new Order();
// Copied into a Java 8 codebase: compile error. `var` didn't exist until
// Java 10 โ the snippet is correct Java, just not correct for THIS codebase.
// AFTER โ knowing the version timeline before writing the line
Order order = new Order();
// Explicit type, because this project targets Java 8 โ verified against
// the version table below before assuming otherwise.
- First release: Java 1.0, January 23, 1996
- Current LTS: Java 25 (September 2025)
- Latest release overall: Java 26 (March 2026, non-LTS)
- Release cycle: Every 6 months since Java 9 (March and September)
- LTS cycle: Every 2 releases as of 21โ25 (previously every 3 years; 8, 11, 17, 21, 25)
- Stewarded by: Oracle since 2010 (previously Sun Microsystems)
Understanding Java's Release Model
Since Java 9, a new feature release ships every six months, but only some of those are designated LTS (Long-Term Support) โ the ones actually worth deploying to production and staying on for years.
| LTS version | Released | Status as of mid-2026 |
|---|---|---|
| Java 8 | Mar 2014 | Still widely deployed; extended support available from several vendors |
| Java 11 | Sep 2018 | Common baseline for applications not yet migrated past the jakarta namespace split |
| Java 17 | Sep 2021 | Common current production baseline |
| Java 21 | Sep 2023 | Previous LTS; still receiving updates |
| Java 25 | Sep 2025 | Current LTS โ at least 5 years of premier support |
| Aspect | LTS versions | Non-LTS versions |
|---|---|---|
| Support duration | Years (5+ premier support from Oracle, longer with commercial support) | 6 months โ until the next release |
| Security updates | Regular patches for years | Until the next release ships, then none |
| Production use | Recommended | For evaluating upcoming features only |
| Examples | 8, 11, 17, 21, 25 | 9, 10, 12โ16, 18โ20, 22โ24, 26 |
Production: an LTS version โ Java 21 or 25 today. Legacy maintenance: Java 8 or 11, if dependencies force it. Learning/evaluating upcoming features: the latest release, LTS or not. Never in production: a non-LTS version โ its security patches stop the moment the next release ships.
The Foundation Era (1996โ2004)
These early releases established Java's core identity โ much of what they introduced is still used exactly as originally designed.
Java 1.0 (January 1996) โ "Oak"
The first public release, developed under the codename "Oak,"
introduced "write once, run anywhere": Applets (running Java in a
browser), AWT for GUIs, the core java.lang/java.io/java.util
classes, automatic garbage collection, and a sandboxed security model
for untrusted code.
Java 1.1 (February 1997)
// Inner classes
public class Outer {
private String message = "Hello";
class Inner {
void printMessage() { System.out.println(message); }
}
}
// JDBC โ database connectivity
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost/shop", "user", "pass");
// Reflection
Class<?> clazz = String.class;
Method[] methods = clazz.getMethods();
Java 1.2 (December 1998) โ rebranded "Java 2"
// The Collections Framework โ still used, structurally, exactly like this
List list = new ArrayList(); // no generics yet โ casting required on read
list.add("Hello");
String s = (String) list.get(0);
Java 1.3 (May 2000) โ "Kestrel"
HotSpot became the default JVM (a major performance jump), JNDI joined the core platform, plus the Java Sound API and RMI-over-IIOP.
Java 1.4 (February 2002) โ "Merlin"
// assert โ design by contract
public void setAge(int age) {
assert age >= 0 : "Age cannot be negative";
}
// Regular expressions, finally
Pattern pattern = Pattern.compile("\\d{3}-\\d{4}");
// Chained exceptions
try { riskyOperation(); }
catch (SQLException e) { throw new DataAccessException("Failed", e); }
The Modern Java Era (2004โ2014)
Java 5 through 8 turned a verbose, ceremonial language into a genuinely expressive one.
Java 5 (September 2004) โ "Tiger"
// Generics โ type safety at compile time
List<String> list = new ArrayList<>();
list.add("Hello");
// list.add(123); // now a compile error, not a runtime ClassCastException
// Enhanced for-loop, autoboxing, enums, varargs, annotations โ all Java 5
public enum OrderStatus { PENDING, SHIPPED, DELIVERED }
@Override
public String toString() { return "Order instance"; }
Java 6 (December 2006) โ "Mustang"
Focused on performance and tooling rather than language syntax: the Scripting API (JSR 223), a Compiler API, JDBC 4.0 driver auto-loading, and pluggable annotation processing.
Java 7 (July 2011) โ "Dolphin"
// Diamond operator
Map<String, List<Integer>> map = new HashMap<>();
// try-with-resources
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
} // closed automatically, even on exception
// Multi-catch
try { riskyOperation(); }
catch (IOException | SQLException e) { logger.error("Failed", e); }
Java 8 (March 2014) โ the functional revolution
Arguably the most important release since Java 5, and still the most widely deployed version in production today.
// Lambda expressions
Runnable task = () -> System.out.println("Hello!");
// Stream API
List<String> names = customers.stream()
.filter(c -> c.getAge() >= 18)
.map(Customer::getName)
.sorted()
.collect(Collectors.toList());
// Optional โ no more silent NullPointerException
Optional<Customer> customer = customerRepository.findById(id);
String name = customer.map(Customer::getName).orElse("Unknown");
// New Date/Time API โ immutable, thread-safe, finally correct
LocalDate today = LocalDate.now();
ZonedDateTime zonedNow = ZonedDateTime.now(ZoneId.of("America/New_York"));
The Rapid Release Era (2017โPresent)
Starting with Java 9, Oracle moved to the current 6-month cadence โ faster features, more attention required to version management.
Java 9 (September 2017) โ the module system
// module-info.java โ Project Jigsaw, the biggest structural change since Java's creation
module com.shop.catalog {
requires java.sql;
exports com.shop.catalog.api;
opens com.shop.catalog.internal to com.shop.framework;
}
// Immutable collection factories
List<String> list = List.of("a", "b", "c");
// Private methods in interfaces, JShell (REPL), Stream.takeWhile/dropWhile
Java 10 (March 2018)
// var โ local variable type inference
var orders = List.of("a", "b", "c");
// NOT allowed: fields, method parameters, return types โ local variables only
Java 11 (September 2018) โ LTS
The first LTS under the new model, and still a common production baseline.
// Standard HTTP Client โ no more third-party dependency for this
HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// New String methods
" hello ".strip(); // "hello" โ Unicode-aware, unlike trim()
"hello".repeat(3);
Java 12โ16 (2019โ2021) โ preview features that became Java 17
// Switch expressions (standard in 14)
String result = switch (day) {
case MONDAY, FRIDAY -> "Work";
case SATURDAY, SUNDAY -> "Rest";
default -> "Unknown";
};
// Text blocks (standard in 15)
String json = """
{ "name": "Alice", "age": 30 }
""";
// Records (standard in 16) โ one line instead of a full data class
record Customer(String name, int age) { }
// Pattern matching for instanceof (standard in 16)
if (obj instanceof String s) {
System.out.println(s.length()); // already cast into s
}
Java 17 (September 2021) โ LTS
// Sealed classes โ the compiler knows every permitted subtype
sealed interface Shape permits Circle, Rectangle, Triangle { }
// Exhaustive switch over a sealed hierarchy โ no default branch needed
double area(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> 0.5 * t.base() * t.height();
};
}
Java 21 (September 2023) โ LTS
// Virtual threads (Project Loom) โ see Application Servers & Deployment for
// the full capacity-planning implications of this
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 1_000_000; i++) {
executor.submit(() -> fetchAndProcess()); // millions of concurrent tasks, cheaply
}
}
// Record patterns โ nested destructuring
record Point(int x, int y) { }
record Line(Point start, Point end) { }
if (obj instanceof Line(Point(var x1, var y1), Point(var x2, var y2))) {
System.out.println("Line from (" + x1 + "," + y1 + ") to (" + x2 + "," + y2 + ")");
}
// Sequenced Collections โ first/last access without index arithmetic
SequencedCollection<String> list = new ArrayList<>();
list.addFirst("first"); list.addLast("last");
SequencedCollection<String> reversed = list.reversed();
Java 21 shipped a preview of string interpolation
(STR."Hello, \{name}!"). It's worth knowing this feature
never became standard โ after extended community
feedback surfaced unresolved design concerns, the OpenJDK team
withdrew it completely from JDK 23 rather than ship something they
weren't confident in. This is genuinely rare: it's the first preview
feature in over five years not to eventually stabilize. It's a useful
reminder that a preview feature (unlike anything already standard)
can be reworked, delayed, or dropped entirely โ don't build
production code around one without accounting for that risk.
Java 22 to Today (2024โ2026)
Java 22โ24 (2024) โ non-LTS, notable groundwork
- The Foreign Function & Memory API reached its final form โ the modern, pure-Java replacement for JNI when interoperating with native libraries
- Stream Gatherers โ custom intermediate stream operations beyond the built-in set
- Continued preview iterations of Structured Concurrency and Scoped Values
Java 25 (September 2025) โ current LTS
// Flexible constructor bodies โ validate BEFORE calling super()
class PremiumCustomer extends Customer {
PremiumCustomer(String name) {
if (name == null) throw new IllegalArgumentException("Name required");
super(name); // previously HAD to be the very first statement โ no validation possible before it
}
}
// Module import declarations โ one line instead of a wall of package imports
import module java.base;
// Compact source files and instance main methods โ no boilerplate for a script
void main() {
System.out.println("Hello from Java 25!");
}
// Scoped Values (finalized) โ a safer, immutable alternative to ThreadLocal
// for passing context through a call tree, particularly relevant alongside
// virtual threads where thousands of concurrent tasks make mutable
// ThreadLocal state a real correctness risk
Java 25 also finalized Generational Shenandoah and Compact Object Headers (both reducing GC overhead and per-object memory cost), and โ a genuinely disruptive removal โ dropped 32-bit x86 support entirely; the JDK is now 64-bit only. Oracle committed to at least 5 years of premier support for this LTS.
Java 26 (March 2026) โ the current release, non-LTS
Ten JEPs, five finalized. No new stable language syntax this round โ the focus is runtime and library work: the Applet API (deprecated for removal since Java 17) is finally gone; the AOT cache introduced under Project Leyden now works with any garbage collector, including ZGC, to reduce startup and warm-up time; and cryptography continues hardening with hybrid public-key encryption and post-quantum-ready JAR signing. Structured Concurrency and Primitive Types in Patterns both continue as previews, still maturing toward a future stable release.
Scheduled as a non-LTS release, with post-quantum-ready TLS 1.3 key exchange already targeted and Project Valhalla's long-anticipated Value Classes and Objects a candidate preview. Value classes โ identity-free types that behave like objects in code but can be laid out in memory like primitives โ represent one of the most significant JVM-level changes on the horizon; when they eventually stabilize, they'll be worth their own dedicated coverage.
Complete Version Reference
| Version | Released | LTS | Key features |
|---|---|---|---|
| 1.0 | Jan 1996 | โ | Applets, AWT, initial release |
| 1.1 | Feb 1997 | โ | Inner classes, JDBC, reflection |
| 1.2 | Dec 1998 | โ | Collections Framework, Swing |
| 1.3 | May 2000 | โ | HotSpot JVM default, JNDI |
| 1.4 | Feb 2002 | โ | assert, regex, NIO |
| 5 | Sep 2004 | โ | Generics, enums, annotations, enhanced for |
| 6 | Dec 2006 | โ | Scripting API, JDBC 4.0 |
| 7 | Jul 2011 | โ | Diamond operator, try-with-resources, multi-catch |
| 8 | Mar 2014 | LTS | Lambdas, Streams, Optional, new Date/Time API |
| 9 | Sep 2017 | โ | Module system, JShell, collection factories |
| 10 | Mar 2018 | โ | var |
| 11 | Sep 2018 | LTS | HTTP Client, new String methods, single-file execution |
| 12โ16 | 2019โ2021 | โ | Switch expressions, text blocks, records (all previewed here, standard in 17 or earlier) |
| 17 | Sep 2021 | LTS | Sealed classes, pattern matching, records standardized |
| 18โ20 | 2022โ2023 | โ | Virtual threads preview, pattern matching enhancements |
| 21 | Sep 2023 | LTS | Virtual threads, record patterns, sequenced collections |
| 22โ24 | 2024 | โ | FFM API finalized, stream gatherers, String Templates withdrawn (23) |
| 25 | Sep 2025 | LTS | Scoped values, flexible constructors, compact source files, 32-bit x86 dropped |
| 26 | Mar 2026 | โ | Applet API removed, GC-agnostic AOT cache, post-quantum crypto hardening |
Migration Guide
Java 8 โ 11
- Add explicit dependencies for the Java EE modules removed from the JDK itself (JAXB, JAX-WS)
--add-opensfor any illegal reflective access the module system now blocks by default- Replace
javax.xml.bindwithjakarta.xml.bind - Update build tooling (Maven 3.5+, Gradle 5+)
Java 11 โ 17
- Strong encapsulation denies illegal reflective access by default โ no opt-out flag as a permanent fix, only a deadline extension
- Can adopt records, sealed classes, pattern matching for
instanceof - Some weaker cryptographic algorithms removed โ audit TLS/cipher configuration
Java 17 โ 21
- Evaluate migrating blocking, thread-per-request code to virtual threads (see Application Servers & Deployment for the full capacity-planning picture)
- Can adopt record patterns and sequenced collections
Java 21 โ 25
- If any experimental code used the String Templates preview from 21/22, it must be removed entirely โ the feature doesn't exist in 23+
- Verify no dependency targets 32-bit x86 โ support was dropped entirely
- Evaluate Scoped Values as a replacement for
ThreadLocalin code that also adopted virtual threads
Common Pitfalls
Non-LTS versions receive exactly 6 months of updates โ after the next release ships, no further security patches arrive at all. Always deploy an LTS version (8, 11, 17, 21, 25) in production.
@Deprecated(since = "9", forRemoval = true)
public void oldMethod() { } // this will actually be removed โ not a stylistic suggestion
Compile with -Xlint:deprecation and resolve warnings
before upgrading, rather than discovering the removal at the
moment of the upgrade itself.
Java guarantees old code runs on new JVMs โ never the reverse.
Code compiled targeting Java 21 will not run on a Java 17 runtime.
Always compile against your actual minimum target version using the
--release flag, covered in full on Source/Target Compatibility.
Interview Questions
Q: What's the difference between Java SE, EE, and ME?
SE (Standard Edition) is the core platform for general-purpose
programming. EE (Enterprise Edition, now Jakarta EE) adds enterprise
APIs โ servlets, JPA, EJB. ME (Micro Edition) targets embedded devices
with limited resources.
Q: What is an LTS version and why does it matter for production?
Long-Term Support versions receive security updates and bug fixes for
years โ 5+ years of premier support from Oracle, often longer with
commercial support. Non-LTS versions only receive 6 months of updates
before being superseded, with no further patches after that. Production
systems should always run an LTS version.
Q: What features did Java 8 introduce, and why were they significant?
Lambda expressions, the Stream API, Optional, and the new
Date/Time API. Together they brought functional-style programming to
Java, enabling more concise collection processing, safer null handling,
and fixing what had been notoriously bad built-in date/time support.
Q: What problem does the Java module system (Java 9+) solve, and what does it cost?
Modules provide strong encapsulation at the package level โ a module
explicitly declares what it exports and what it requires, rather than
every public class on the classpath being accessible to everything else
("JAR hell" and unintentional API surface). This also lets the JDK
itself be modular, enabling minimal custom runtime images via
jlink. The cost is real migration friction: strong
encapsulation denies reflective access that used to work silently,
which is why --add-opens and dependency audits are a
standing part of any pre-Java-9-to-modern migration.
Q: Why is it notable that String Templates were withdrawn in Java 23 rather than simply delayed again?
Preview features are explicitly designed to be impermanent โ the JEP
process exists precisely so real-world feedback can surface problems
before a feature becomes permanent and effectively unchangeable. String
Templates had been previewed across Java 21 and 22, but extended
community discussion surfaced unresolved design concerns serious enough
that the OpenJDK team chose to withdraw the feature entirely from Java
23 and revisit the design from scratch, rather than ship something they
weren't confident in. This had been the first preview feature in over
five years not to eventually stabilize โ a useful, concrete reminder that
"it shipped as a preview" is not the same guarantee as "it will ship as
stable," and production code should never be built around an unstabilized
preview feature without accounting for that risk.
Q: What's the class file major version for Java 17, and how does a JVM use it?
61. The formula is straightforward once you know two anchor points โ
Java 8 is 52, and each subsequent version adds one (Java 21 is 65, Java
25 is 69). The JVM checks this number against its own maximum supported
version before ever attempting to execute a class file; a class file
with a higher major version than the running JVM supports is rejected
outright with UnsupportedClassVersionError, rather than
attempting to interpret bytecode it might not fully understand. This
mechanism, and its practical consequences, are covered in full on
Version Compatibility.