Java Version History

Three decades of evolution from 1.0 to today's LTS, and why knowing exactly which version introduced a feature is practical knowledge, not trivia

← Back to Index

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.
Key facts, as of mid-2026
  • 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 versionReleasedStatus as of mid-2026
Java 8Mar 2014Still widely deployed; extended support available from several vendors
Java 11Sep 2018Common baseline for applications not yet migrated past the jakarta namespace split
Java 17Sep 2021Common current production baseline
Java 21Sep 2023Previous LTS; still receiving updates
Java 25Sep 2025Current LTS โ€” at least 5 years of premier support
AspectLTS versionsNon-LTS versions
Support durationYears (5+ premier support from Oracle, longer with commercial support)6 months โ€” until the next release
Security updatesRegular patches for yearsUntil the next release ships, then none
Production useRecommendedFor evaluating upcoming features only
Examples8, 11, 17, 21, 259, 10, 12โ€“16, 18โ€“20, 22โ€“24, 26
Choosing a version

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();
String Templates were previewed in 21 and 22 โ€” then withdrawn entirely in 23

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.

What's next: Java 27 (September 2026)

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

VersionReleasedLTSKey features
1.0Jan 1996โ€”Applets, AWT, initial release
1.1Feb 1997โ€”Inner classes, JDBC, reflection
1.2Dec 1998โ€”Collections Framework, Swing
1.3May 2000โ€”HotSpot JVM default, JNDI
1.4Feb 2002โ€”assert, regex, NIO
5Sep 2004โ€”Generics, enums, annotations, enhanced for
6Dec 2006โ€”Scripting API, JDBC 4.0
7Jul 2011โ€”Diamond operator, try-with-resources, multi-catch
8Mar 2014LTSLambdas, Streams, Optional, new Date/Time API
9Sep 2017โ€”Module system, JShell, collection factories
10Mar 2018โ€”var
11Sep 2018LTSHTTP Client, new String methods, single-file execution
12โ€“162019โ€“2021โ€”Switch expressions, text blocks, records (all previewed here, standard in 17 or earlier)
17Sep 2021LTSSealed classes, pattern matching, records standardized
18โ€“202022โ€“2023โ€”Virtual threads preview, pattern matching enhancements
21Sep 2023LTSVirtual threads, record patterns, sequenced collections
22โ€“242024โ€”FFM API finalized, stream gatherers, String Templates withdrawn (23)
25Sep 2025LTSScoped values, flexible constructors, compact source files, 32-bit x86 dropped
26Mar 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-opens for any illegal reflective access the module system now blocks by default
  • Replace javax.xml.bind with jakarta.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 ThreadLocal in code that also adopted virtual threads

Common Pitfalls

Deploying a non-LTS version to production

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.

Ignoring deprecation warnings
@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.

Assuming forward compatibility

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

๐ŸŽ“ Junior level

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.

๐Ÿ”ฅ Senior level

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.