Skip to content

Java Since Java 8: What Has Changed? A Guide for Getting Back In

Published on Feb 27, 2026 | approx. 13 min read |

As of: February 2026

Typical Starting Point

  • Java 6 to 8, Swing for desktop GUIs
  • A single JVM/JDK source: Oracle (formerly Sun Microsystems)
  • Maven as build tool
  • IDE: NetBeans, Eclipse, IntelliJ IDEA

1. The Java Landscape Has Changed Fundamentally

1.1 Release Cycle

The biggest structural change: Java now ships every 6 months (March and September). Every 2 years there is a Long-Term Support (LTS) release with at least 3 years of updates.

Starting point (2014)                Today (2025/2026)
────────────────                     ──────────────────
Java 8 (LTS, 2014)                   Java 25 (current, LTS, Sep 2025)
Next version: wait ??? years         New version every 6 months
One provider: Oracle/Sun             Dozens of JDK distributions
JRE separately installable           JRE no longer exists separately

1.2 The LTS Versions (Key Milestones)

Version Year Status Most Important Innovation
Java 8 2014 Common starting point — still widely used Lambdas, Streams, Optional
Java 9 2017 Non-LTS Module System (Project Jigsaw)
Java 11 2018 LTS var in lambdas, HTTP client, JRE removed
Java 17 2021 LTS Sealed Classes, Records, Pattern Matching
Java 21 2023 LTS ⭐ (current recommendation) Virtual Threads, Pattern Matching for switch, Record Patterns
Java 25 Sep 2025 LTS ⭐ (current) Further stabilisations

Recommendation: Java 21 is the current LTS and should be used for new projects. Java 8 and 11 are still heavily present in legacy projects.


2. JDK Distributions: Oracle Is No Longer the Only Provider

2.1 What Happened?

After Oracle acquired Sun Microsystems (2010), Oracle changed the licensing policy multiple times. Since 2019, the Oracle JDK has been paid for commercial use (with occasional relaxations). This led to many alternative JDK distributions emerging — all based on the open OpenJDK source code.

2.2 The Most Important JDK Distributions

Distribution Provider Cost Distinguishing Feature
Eclipse Temurin Eclipse Foundation (Adoptium) Free Recommendation. Community-driven, TCK-tested, backed by IBM/Red Hat/Microsoft
Amazon Corretto Amazon Free Optimised for AWS cloud, LTS support
Oracle JDK Oracle Free for development, paid for production (changing licences!) The "original" build
Oracle OpenJDK Oracle Free Reference implementation, only 6 months of support per version
Azul Zulu Azul Systems Community version free Very broad platform support
Microsoft OpenJDK Microsoft Free Optimised for Azure
Red Hat OpenJDK Red Hat Free (RHEL) Standard on RHEL/Fedora/CentOS
BellSoft Liberica BellSoft Free Only distribution with built-in JavaFX
SAP Machine SAP Free Optimised for SAP environments
IBM Semeru IBM Free Uses OpenJ9 instead of HotSpot (alternative JVM)
GraalVM Oracle Community version free Ahead-of-time compilation, native images, polyglot

2.3 Which Distribution to Choose?

The recommendation is: Eclipse Temurin (formerly AdoptOpenJDK). It is the de facto standard for OpenJDK distributions, completely free, TCK-tested, and supported by all major IDEs.

Installation via SDKMAN (recommended — manages multiple Java versions):

# Install SDKMAN
curl -s "https://get.sdkman.io" | bash

# Install Java 21 (Temurin)
sdk install java 21.0.4-tem

# Switch between versions
sdk use java 21.0.4-tem
sdk use java 17.0.12-tem

# Show all available distributions
sdk list java

2.4 The JRE No Longer Exists

Since Java 11, Oracle no longer ships a separate JRE. Instead, there is jlink, a tool that creates a custom runtime containing only the required modules — significantly smaller than a full JRE.


3. The Most Important Syntax Innovations (Java 9 to 21)

3.1 Comparison: Old vs. New Code

// ══════════════════════════════════════════════════
// KLASSISCHER STIL (Java 8)
// ══════════════════════════════════════════════════

package com.example;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class BenutzerService {

    public static void main(String[] args) {
        List<Benutzer> benutzer = new ArrayList<>();
        benutzer.add(new Benutzer("Max", "Müller", 30));
        benutzer.add(new Benutzer("Anna", "Schmidt", 25));

        // Stream API (Java 8)
        List<String> namen = benutzer.stream()
            .filter(b -> b.getAlter() > 20)
            .map(b -> b.getVorname() + " " + b.getNachname())
            .collect(Collectors.toList());

        for (String name : namen) {
            System.out.println(name);
        }
    }
}

class Benutzer {
    private String vorname;
    private String nachname;
    private int alter;

    public Benutzer(String vorname, String nachname, int alter) {
        this.vorname = vorname;
        this.nachname = nachname;
        this.alter = alter;
    }

    public String getVorname() { return vorname; }
    public String getNachname() { return nachname; }
    public int getAlter() { return alter; }

    @Override
    public boolean equals(Object o) { /* 15 Zeilen Boilerplate */ }

    @Override
    public int hashCode() { /* Noch mehr Boilerplate */ }

    @Override
    public String toString() {
        return "Benutzer{vorname='" + vorname + "', nachname='" + nachname
               + "', alter=" + alter + "}";
    }
}


// ══════════════════════════════════════════════════
// MODERNER STIL (Java 21)
// ══════════════════════════════════════════════════

package com.example;

// Record statt Klasse (Java 16) — immutable, equals/hashCode/toString automatisch
record Benutzer(String vorname, String nachname, int alter) {}

class BenutzerService {
    public static void main(String[] args) {
        // List.of() statt new ArrayList<> (Java 9)
        var benutzer = List.of(    // var statt explizitem Typ (Java 10)
            new Benutzer("Max", "Müller", 30),
            new Benutzer("Anna", "Schmidt", 25)
        );

        // .toList() statt .collect(Collectors.toList()) (Java 16)
        var namen = benutzer.stream()
            .filter(b -> b.alter() > 20)    // b.alter() statt b.getAlter()
            .map(b -> b.vorname() + " " + b.nachname())
            .toList();

        namen.forEach(System.out::println);
    }
}

The entire Benutzer class (38 lines) has been reduced to a single line. That is the most visible difference.

3.2 Feature Overview by Version

Java 9 (2017) — The Module System

// module-info.java — neues Konzept, nicht rückwärtskompatibel
module com.example.app {
    requires java.net.http;
    exports com.example.api;
}

// Fabrikmethoden für Collections (endlich!)
List<String> list = List.of("a", "b", "c");           // Unveränderlich!
Set<String> set = Set.of("a", "b", "c");
Map<String, Integer> map = Map.of("key", 1, "key2", 2);

// Private Methoden in Interfaces
interface Greeter {
    default void greetAll(List<String> names) {
        names.forEach(this::greet);
    }
    private void greet(String name) {
        System.out.println("Hallo " + name);
    }
}

The module system (Project Jigsaw) was the biggest structural change since Java 1.0. In practice, however, many projects do not actively use it — most applications still run on the unnamed module/classpath.

Java 10 (2018) — var

// Lokale Typinferenz — der Compiler erkennt den Typ automatisch
var benutzer = new Benutzer("Max", "Müller", 30);  // statt Benutzer benutzer = ...
var liste = new ArrayList<String>();                 // statt ArrayList<String> liste = ...
var stream = liste.stream();                         // statt Stream<String> stream = ...

// WICHTIG: var ist KEIN dynamischer Typ wie in JavaScript!
// Der Typ wird zur Compile-Zeit festgelegt, nicht zur Laufzeit.
// var funktioniert NUR für lokale Variablen, nicht für Felder oder Parameter.

Java 11 (2018, LTS) — New HTTP Client, String Methods

// Eingebauter HTTP-Client (ersetzt das uralte HttpURLConnection)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users"))
    .header("Content-Type", "application/json")
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());

// Neue String-Methoden
"  Hallo  ".strip();          // "Hallo" (wie trim, aber Unicode-aware)
"  ".isBlank();                // true
"Zeile1\nZeile2".lines();     // Stream<String>
"Ha".repeat(3);                // "HaHaHa"

// var in Lambda-Parametern (für Annotationen nützlich)
list.stream()
    .map((@NotNull var s) -> s.toUpperCase())
    .toList();

Java 14 (2020) — Switch Expressions, Helpful NullPointerExceptions

// Switch als Expression (gibt einen Wert zurück!)
int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY                -> 7;
    case WEDNESDAY              -> 9;
    default -> {
        String s = day.toString();
        yield s.length();  // yield statt return in Blöcken
    }
};

// Endlich hilfreiche NullPointerExceptions!
// Vorher: "NullPointerException" (wo genau?!)
// Jetzt:  "Cannot invoke String.toLowerCase() because the return
//          value of User.getName() is null"

Java 15 (2020) — Text Blocks

// Mehrzeilige Strings (kein "\\n" + Konkatenation mehr nötig)
String json = """
        {
            "name": "Max",
            "alter": 30,
            "stadt": "Berlin"
        }
        """;

String html = """
        <html>
            <body>
                <p>Hallo Welt</p>
            </body>
        </html>
        """;

// Sealed Classes (Preview in Java 15, stabil ab Java 17)
// Beschränken welche Klassen eine Klasse erweitern dürfen (siehe Java 17 für stabile Version)
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}

Java 16 (2021) — Records, Pattern Matching for instanceof

// Records — immutable Datenklassen in einer Zeile
// Erzeugt automatisch: Konstruktor, Getter, equals(), hashCode(), toString()
record Point(int x, int y) {}

var p = new Point(3, 4);
p.x();        // 3 (Achtung: x() statt getX()!)
p.y();        // 4

// Records können auch Validierung haben:
record Alter(int wert) {
    Alter {  // "Compact Constructor"
        if (wert < 0) throw new IllegalArgumentException("Alter darf nicht negativ sein");
    }
}

// Pattern Matching for instanceof (kein Cast mehr nötig!)
// Vorher (Java 8):
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

// Jetzt (Java 16+):
if (obj instanceof String s) {
    System.out.println(s.length());  // s ist automatisch gecastet!
}

// Stream.toList() — kein Collectors.toList() mehr nötig
var namen = stream.map(String::toUpperCase).toList();

Java 17 (2021, LTS) — Sealed Classes

// Sealed Classes: Der Compiler kennt ALLE möglichen Subtypen
sealed interface Ergebnis permits Erfolg, Fehler {}
record Erfolg(String daten) implements Ergebnis {}
record Fehler(String nachricht) implements Ergebnis {}

// Zusammen mit Pattern Matching wird das sehr mächtig:
String verarbeite(Ergebnis e) {
    return switch (e) {
        case Erfolg(var daten)      -> "OK: " + daten;
        case Fehler(var nachricht)  -> "Fehler: " + nachricht;
        // Kein default nötig — Compiler weiß, dass alle Fälle abgedeckt sind!
    };
}

Java 21 (2023, LTS) — Virtual Threads, Pattern Matching for switch ⭐

// ═══ VIRTUAL THREADS — die größte Neuerung seit Lambdas ═══
// Vorher: Ein OS-Thread pro Task (teuer, ~1MB Stack pro Thread)
// Jetzt: Millionen von Virtual Threads möglich

// Einen Virtual Thread starten:
Thread.startVirtualThread(() -> {
    System.out.println("Läuft auf einem virtuellen Thread!");
});

// Mit Executor:
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 1_000_000; i++) {
        executor.submit(() -> {
            Thread.sleep(Duration.ofSeconds(1));
            return "Fertig";
        });
    }
}
// 1 Million gleichzeitige Tasks — mit klassischen Threads unmöglich!

// In Spring Boot 3.2+ reicht eine Zeile in application.properties:
// spring.threads.virtual.enabled=true
// → Alle Requests laufen automatisch auf Virtual Threads


// ═══ PATTERN MATCHING FOR SWITCH — voll ausgereift ═══
String formatiere(Object obj) {
    return switch (obj) {
        case Integer i when i > 0  -> "Positive Zahl: " + i;
        case Integer i             -> "Negative oder Null: " + i;
        case String s              -> "Text: " + s;
        case null                  -> "null!";
        default                    -> "Unbekannt: " + obj;
    };
}

// ═══ RECORD PATTERNS — Destrukturierung ═══
record Point(int x, int y) {}

// Direkt in Pattern Matching destrukturieren:
if (obj instanceof Point(int x, int y)) {
    System.out.println("x=" + x + ", y=" + y);
}

// ═══ SEQUENCED COLLECTIONS — endlich einheitlicher Zugriff ═══
SequencedCollection<String> list = new ArrayList<>(List.of("A", "B", "C"));
list.getFirst();     // "A"
list.getLast();       // "C"
list.reversed();     // ["C", "B", "A"]

// ═══ UNNAMED VARIABLES — Unterstrich für ungenutzte Variablen ═══
try {
    int i = Integer.parseInt(input);
} catch (NumberFormatException _) {    // _ statt "e" wenn nicht verwendet
    System.out.println("Ungültige Zahl");
}

Virtual threads are particularly relevant for highly parallel server applications: Spring Boot on Java 21 with virtual threads can serve hundreds of thousands of concurrent HTTP requests with minimal resource consumption — similar to Go's goroutines, but in Java.


4. GUI Libraries: Swing Is Dead, Long Live JavaFX

4.1 Current Status

Framework Status Recommendation
AWT Ancient, feature-frozen Do not use
Swing Feature-frozen, only bug fixes Only for legacy maintenance. IntelliJ IDEA is still based on it
JavaFX (OpenJFX) Actively maintained, 2 releases/year ⭐ Standard for new desktop apps
SWT Eclipse-based, future uncertain Wait and see
Compose Desktop JetBrains (Kotlin-based) Modern, declarative — like Flutter for Java/Kotlin
Vaadin Web framework, server-side Java-only alternative to JS frontends

4.2 What Happened to JavaFX?

Since Java 11, JavaFX is no longer part of the JDK. It was spun off as a standalone open-source project (OpenJFX) and is actively developed by the community and Gluon. This means:

  • JavaFX is included as a Maven/Gradle dependency, instead of being "just there"
  • JavaFX has its own versioning (currently version 23/24)
  • Only exception: BellSoft Liberica JDK still includes JavaFX built-in
<!-- JavaFX als Maven-Dependency einbinden -->
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-controls</artifactId>
    <version>21.0.2</version>
</dependency>

4.3 Desktop Applications in Java Today

Desktop development with Java is significantly less common than in earlier years. Most applications are web-based today. The main exceptions are IDEs (IntelliJ, Eclipse, NetBeans) and specialised enterprise software.

For new desktop apps in the JVM world, Compose Desktop (Kotlin, by JetBrains) is the most modern approach — declarative UI similar to Svelte or React, but in Kotlin.


5. Build Tools: Maven Lives On, Gradle Is Catching Up

5.1 Maven Today

Maven still exists and remains the most widely used build tool in the Java world. The basic structure (pom.xml, lifecycle, repositories) has barely changed since the Java 8 era. If you knew Maven 3 then, you still know it today.

Recent developments:

  • Maven 4 is in development (for years now) and aims to modernise the POM format
  • Maven Wrapper (mvnw) — similar to Gradle Wrapper: the Maven version is bundled with the project, so every developer uses exactly the same version
  • Maven Daemon (mvnd) — keeps a JVM running for faster builds (inspired by Gradle)
# Maven Wrapper generieren (mit dem Maven Wrapper Plugin)
mvn wrapper:wrapper

# Dann statt "mvn" einfach:
./mvnw clean install

5.2 Gradle as an Alternative

Gradle is the challenger that has gained massive adoption primarily through Android development (Google chose Gradle as the standard build tool for Android).

Aspect Maven Gradle
Configuration language XML (pom.xml) Groovy or Kotlin DSL (build.gradle.kts)
Philosophy Convention over Configuration (rigid) Flexible, programmable
Performance Good, but no caching Incremental builds, build cache, daemon — 2-5x faster
Adoption (2025) ~52% of Java projects ~48% of Java projects
Learning curve Easier, because more rigid Steeper, because more flexible
Plugin ecosystem Huge, more plugins available Large, but slightly smaller
IDE integration Excellent in all IDEs Very good, excellent in IntelliJ
// build.gradle.kts (Kotlin DSL) — zum Vergleich mit pom.xml
plugins {
    java
    id("org.springframework.boot") version "3.2.0"
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.postgresql:postgresql:42.7.1")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

5.3 Recommendation

If you already have Maven experience, it is worth sticking with it. It works, you know it, and for a Spring Boot project it makes no practical difference. Gradle is mainly worthwhile for very large multi-module projects or for Android development.


6. The Ecosystem: Frameworks and Libraries

6.1 Web Frameworks

Framework Description Status 2025
Spring Boot The dominant Java web framework ⭐ De facto standard. Version 3.x requires Java 17+
Jakarta EE (formerly Java EE) Enterprise standard Still alive, was handed from Oracle to the Eclipse Foundation. "javax." → "jakarta."
Quarkus "Supersonic Subatomic Java" from Red Hat Rising. Optimised for cloud/containers, GraalVM native images
Micronaut Compile-time DI, no reflection Rising. Competitor to Quarkus
Vert.x Reactive, event-driven framework Niche, but high-performance

6.2 Jakarta EE: The Name Change

Java EE was handed from Oracle to the Eclipse Foundation and is now called Jakarta EE. The most important practical consequence: all package names have changed.

// Vorher (Java EE):
import javax.servlet.http.HttpServlet;
import javax.persistence.Entity;

// Jetzt (Jakarta EE):
import jakarta.servlet.http.HttpServlet;
import jakarta.persistence.Entity;

This also affects JPA/Hibernate, Servlets, JAX-RS, CDI, etc. Spring Boot 3.x requires Jakarta EE — so upgrading from Spring Boot 2.x to 3.x also means this namespace change.

6.3 JVM Languages

The JVM today is not just for Java. Several languages run on the same VM:

Language Use Case
Kotlin Android (officially recommended by Google), server-side (Spring supports Kotlin first-class), multi-platform
Scala Big Data (Apache Spark), functional programming
Groovy Gradle build scripts, Jenkins pipelines, scripting
Clojure Functional programming (Lisp on the JVM)

Kotlin is the most significant development here: it is essentially a "better Java" with null safety, coroutines, extension functions, and significantly less boilerplate. Many Spring Boot projects are now written in Kotlin instead of Java.

6.4 Important Libraries (What Has Changed)

Area Then (Java 8) Today
JSON Jackson, Gson, manually included Jackson (built into Spring Boot), Gson still popular
HTTP client Apache HttpClient, HttpURLConnection java.net.http.HttpClient (built-in since Java 11!)
Logging Log4j, SLF4J + Logback SLF4J + Logback (default in Spring Boot). Log4j 1 is EOL, Log4j2 had a critical security vulnerability in 2021
Testing JUnit 4 JUnit 5 (completely new, modular architecture, @Test from org.junit.jupiter)
ORM Hibernate 4/5 Hibernate 6 (with Jakarta EE namespace), Spring Data JPA abstracts much of it
Reactive Non-existent Project Reactor (Spring WebFlux), RxJava
Dependency Injection Spring Framework, CDI Spring Boot (auto-configuration), Quarkus/Micronaut (compile-time DI)
Database migration Manual or Liquibase Flyway (most popular) or Liquibase

7. Garbage Collectors: New Generation

The JVM today has several highly optimised garbage collectors:

GC Since Characteristic
G1 Java 9 (default) Balanced, good all-rounder — active by default
ZGC Java 15 (stable), Java 21 (generational) Ultra-low pause times (<1ms), even with huge heaps
Shenandoah Java 15 (stable) Similar to ZGC, developed by Red Hat

The CMS (Concurrent Mark Sweep) and the Parallel GC from earlier Java versions have been replaced by G1. ZGC and Shenandoah are the future for latency-sensitive applications.


8. GraalVM and Native Images

One of the most exciting developments: GraalVM can compile Java applications into native binaries (ahead-of-time compilation). The result starts in milliseconds instead of seconds and uses significantly less memory.

# Native Image bauen (mit Spring Boot 3 + GraalVM)
./mvnw -Pnative native:compile

# Ergebnis: Eine eigenständige Binary (~50-80 MB)
./target/mein-service    # Startet in ~50ms statt 2-3 Sekunden

Frameworks like Quarkus and Micronaut are explicitly optimised for this. Spring Boot has supported native images since version 3.0, but with limitations (reflection-based features do not work without configuration).


9. IDEs for Java Today

IDE Status Cost
IntelliJ IDEA Community ⭐ Most popular Java IDE Free (open source)
IntelliJ IDEA Ultimate Full version with web/DB/framework support Paid (EUR 599/year, or ~EUR 199/year for individuals)
Eclipse Still active, but declining Free
NetBeans Now an Apache project, less widespread Free
VS Code + Java Extensions Works, but not IDE-level Free

The IntelliJ IDEA Community Edition is perfectly sufficient for most Java projects. Those who use Spring Boot intensively benefit from the Ultimate Edition (Spring support, database tools, HTTP client). There is a 30-day trial version.


10. Summary: What Has Changed

The Top 10 Changes Since Java 8

  1. Records (Java 16) — Immutable data classes in a single line
  2. var (Java 10) — Local type inference
  3. Text Blocks (Java 15) — Multi-line strings with """
  4. Pattern Matching (Java 16/21) — instanceof without cast, switch with types
  5. Sealed Classes (Java 17) — Restricted inheritance
  6. Switch Expressions (Java 14) — Switch returns values
  7. Virtual Threads (Java 21) — Millions of lightweight threads
  8. Module System (Java 9) — Exists, but often ignored
  9. New Collection MethodsList.of(), .toList(), getFirst()/getLast()
  10. HTTP Client (Java 11) — Finally a modern built-in HTTP client

What Has NOT Changed

  • Java is still statically typed and object-oriented
  • The JVM is still the JVM (HotSpot)
  • Maven pom.xml still looks the same
  • Classes, interfaces, inheritance, generics — all unchanged
  • public static void main(String[] args) still works
  • The ecosystem is massive and backward-compatible

The Short Version

Recommended distribution: Eclipse Temurin Java 21 (via SDKMAN)
Build tool:               Maven (proven) or Gradle (for large projects)
Framework:                Spring Boot 3.x
IDE:                      IntelliJ IDEA Community (or Ultimate for Spring support)
GUI:                      JavaFX (if desktop is needed)
Testing:                  JUnit 5

Appendix: Learning Resources

  • "New language features since Java 8 to 21" — Compact overview with code examples https://advancedweb.hu/new-language-features-since-java-8-to-21/
  • WhichJDK.com — Which JDK distribution should I choose? https://whichjdk.com/
  • Baeldung — The most comprehensive Java tutorial portal https://www.baeldung.com
  • Spring Boot Guides — Official getting-started guides https://spring.io/guides
  • JDK Comparison — Detailed comparison of all JDK distributions https://jdkcomparison.com/
  • SDKMAN — Java version manager for Linux/macOS https://sdkman.io
Thomas Wunner

Thomas Wunner

Certified IT specialist for application development with an instructor qualification and over 14 years of experience building scalable web applications with Symfony and Shopware. When not coding, Thomas volunteers as a lifeguard with the Wasserwacht, performs as a DJ, and explores the countryside on his motorbike.

Comments

Comments are provided by Remark42. By loading comments, data is transmitted to our comment server.