Java wird ständig besser und kaum jemand merkt es
Der Sechs-Monate-Release-Rhythmus hat Java still verändert. Pattern Matching, virtuelle Threads und Records machen die Sprache kaum wiedererkennbar.

Java ist das Rodney Dangerfield unter den Programmiersprachen: Es bekommt keinen Respekt. Erwähnt man Java vor den meisten Entwicklern unter 30, denken sie sofort an XML-Konfigurationsdateien, Enterprise Beans und AbstractSingletonProxyFactoryBean. Der Ruf der Sprache ist im Jahr 2008 eingefroren, als Java 6 die aktuelle Version war und Java-Programmieren bedeutete, in Boilerplate-Code zu versinken.
Doch seit Java 2017 auf einen Sechs-Monate-Release-Rhythmus umgestellt wurde, ist etwas Bemerkenswertes passiert. Die Sprache verbessert sich in einem Tempo, das in den vierjährigen Lücken zwischen Java 6, 7 und 8 undenkbar gewesen wäre. Records. Sealed Classes. Pattern Matching. Virtual Threads. String Templates. Structured Concurrency. Jedes Release bringt Funktionen, die Java-Code kürzer, ausdrucksstärker und angenehmer zu schreiben machen. Java 26 setzt diese Serie fort, und die Kluft zwischen „Java, wie es ist“ und „Java, wie Leute es sich vorstellen“ war nie größer.
Die Features, die alles verändert haben
Wenn du seit Java 8 kein Java mehr geschrieben hast, verpasst du ein ganzes Jahrzehnt an Verbesserungen. So sieht die Sprache heute aus.
Records (Java 14) sind unveränderliche Datenklassen. Was früher 50 Zeilen Boilerplate verlangte – Felder, Konstruktor, Getter, equals, hashCode, toString – ist jetzt eine einzige Zeile.
// Java 8: 50 lines of boilerplate
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Point)) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
@Override
public int hashCode() { return Objects.hash(x, y); }
@Override
public String toString() { return "Point[x=" + x + ", y=" + y + "]"; }
}
// Java 14+: one line
record Point(int x, int y) {}
Pattern Matching (Java 16–21, noch in Entwicklung) erlaubt es, Objekte in Switch-Expressions und instanceof-Prüfungen zu destrukturieren. Damit verschwinden die verschachtelten if-else-instanceof-Ketten, die Java-Code so lange geplagt haben.
// Pattern matching with sealed types and records
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 {}
// Exhaustive pattern matching — compiler verifies all cases covered
double area(Shape shape) {
return switch (shape) {
case Circle(var r) -> Math.PI * r * r;
case Rectangle(var w, var h) -> w * h;
case Triangle(var b, var h) -> 0.5 * b * h;
};
}
Virtual Threads (Java 21) sind leichtgewichtige Threads, die von der JVM statt vom Betriebssystem verwaltet werden. Man kann Millionen davon erzeugen, ohne an Speicher- oder Betriebssystem-Thread-Handles zu stoßen. Dadurch wird das Modell „ein Thread pro Request“ – das einfachste Concurrency-Modell überhaupt – auch im großen Maßstab praktikabel.
// Create a million concurrent tasks — try this with OS threads
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 1_000_000).forEach(i -> {
executor.submit(() -> {
// Each task gets its own virtual thread
// Blocking I/O (HTTP, DB, file) automatically yields
var response = httpClient.send(request, BodyHandlers.ofString());
return response.body();
});
});
}
// All done — executor auto-closes, waits for completion
Virtual Threads sind Javas Antwort auf Gos Goroutinen und Kotlins Coroutinen. Der Unterschied: Man braucht keine spezielle Syntax und muss Funktionen nicht „färben“. Regulärer blockierender Code funktioniert einfach – die JVM hält virtuelle Threads an blockierenden Stellen transparent an und setzt sie wieder fort. Dein bestehender blockierender HTTP-Client, JDBC-Treiber und Datei-I/O-Code profitiert davon, ohne dass du etwas ändern musst.
Warum es kaum jemand bemerkt
Javas Entwicklung ist wirklich beeindruckend. Warum steckt die Wahrnehmung dann trotzdem noch im Jahr 2008 fest?
Erstens die Trägheit in Unternehmen. Viele Organisationen betreiben Java 8 oder 11, weil die Migration einer großen Codebasis teuer und riskant ist. Wenn Entwickler über „Java“ sprechen, meinen sie die Version, die ihre Firma nutzt, nicht das neueste Release. Java-8-Entwickler haben Records, Pattern Matching oder Virtual Threads nie erlebt.
Zweitens die Framework-Schicht. Spring Boot, Jakarta EE und andere Enterprise-Frameworks legen ihre eigene Komplexität über die Sprache. Entwickler machen „Java“ für das Annotations-Zeremoniell von Spring verantwortlich, doch das liegt am Framework, nicht an der Sprache. Modernes Java ohne schweres Framework ist überraschend schlank.
Drittens die kulturelle Trägheit. Die Techszene ordnet Sprachen Stämmen zu. Java gilt als Enterprise-Sprache, verbunden mit Großkonzernen, bürokratischen Prozessen und Over-Engineering. Dieser Ruf wurde verdient – Java-Entwicklung um 2005 hatte die Kritik verdient. Aber Ruf überlebt seine Ursachen oft um Jahre.
Der Vorteil der JVM
Die Sprachverbesserungen sind wichtig, aber Javas größter Vorteil bleibt die JVM. Nach drei Jahrzehnten Optimierung ist die HotSpot-JVM eine der ausgefeiltesten Laufzeitumgebungen, die je gebaut wurden.
- JIT-Kompilierung. Die JVM analysiert laufenden Code und kompiliert heiße Methoden zu optimiertem Maschinencode. Bei langlaufenden Anwendungen (Server, Datenverarbeitung) erreicht die JVM oft die Leistung von C++ oder übertrifft sie, weil der JIT auf das tatsächliche Laufzeitverhalten optimieren kann und nicht nur auf statische Analysen angewiesen ist.
- Garbage Collection. Moderne GC-Implementierungen (ZGC, Shenandoah) bieten Pausenzeiten im Sub-Millisekundenbereich, selbst bei Terabyte-großen Heaps. Die Kritik, GC-Pausen würden die Latenz killen, stimmt seit Jahren nicht mehr – diese Collector laufen nebenläufig zur Anwendung.
- Observability. JFR (Java Flight Recorder) bietet produktionstaugliches Profiling mit nahezu null Overhead. Du kannst CPU-Auslastung, Speicherallokation, Lock-Contention, I/O-Latenz und GC-Verhalten messen, ohne die Anwendung auszubremsen.
- Ökosystem-Reife. Maven Central umfasst Millionen Pakete. Die Build-Tools (Gradle, Maven) sind gut verstanden. Das Testökosystem (JUnit, Mockito, Testcontainers) ist hervorragend. Die Deployment-Story (Container, GraalVM Native Images) ist deutlich gereift.
GraalVM und Native Images
Javas größte Schwäche war schon immer die Startzeit. Die JVM braucht hunderte Millisekunden, um sich zu initialisieren, Klassen zu laden und den JIT-Compiler aufzuwärmen. Für Serverless-Funktionen und CLI-Tools ist dieser Overhead nicht akzeptabel.
GraalVM Native Image setzt hier an, indem es Java-Anwendungen im Voraus (Ahead-of-Time) zu eigenständigen ausführbaren Dateien kompiliert. Keine JVM nötig. Die Startzeit sinkt von hunderten Millisekunden auf einstellige Millisekunden. Der Speicherverbrauch sinkt ebenfalls, weil kein Interpreter, kein JIT-Compiler und keine Klassen-Metadaten geladen werden müssen.
Der Haken: Native Images verlieren die adaptive Optimierung der JVM. Eine JIT-kompilierte Anwendung wird mit der Zeit schneller, weil die JVM die heißen Pfade lernt. Ein Native Image startet schnell, verbessert sich aber nicht mehr. Für kurzlebige Prozesse (CLI-Tools, Serverless) gewinnen Native Images. Für langlaufende Server erzeugt der JIT der JVM irgendwann schnelleren Code. Wähle das passende Werkzeug für die Laufzeiteigenschaften deiner Anwendung.
Modernes Java im Vergleich zur Konkurrenz
Die Sprachen, die Java angeblich ersetzt haben – Go, Kotlin, Rust – sind hervorragend. Doch modernes Java konkurriert erfolgreicher, als viele glauben.
Gos Einfachheit ist attraktiv, aber Java hat jetzt Virtual Threads (auf Augenhöhe mit Goroutinen) und ein deutlich reichhaltigeres Typsystem. Typsysteme sind für große Codebasen wichtig, und Javas Typsystem – besonders mit Sealed Types und Pattern Matching – fängt Fehler ab, die Gos Interface-System übersieht.
Kotlin verbessert Javas Syntax, läuft aber auf derselben JVM. Die meisten Vorteile von Kotlin gegenüber Java 8 (Null-Sicherheit, Datenklassen, Coroutinen) haben in modernem Java teilweise oder vollständige Entsprechungen (Optional-Muster, Records, Virtual Threads). Kotlin ist nach wie vor prägnanter, doch der Abstand ist deutlich geschrumpft.
Rust besetzt eine andere Nische – Systemprogrammierung ohne Garbage Collector. Java sollte dort nicht konkurrieren. Doch für serverseitige Anwendungen, Datenverarbeitung und Unternehmenssoftware ist die Kombination aus Leistung, Sicherheit und Ökosystem-Reife von Java schwer zu schlagen.
Der Sechs-Monate-Rhythmus ist die eigentliche Innovation
Mehr als jedes einzelne Feature hat der Sechs-Monate-Release-Zyklus Javas Entwicklung verändert. Vor 2017 waren Java-Releases Ereignisse im Abstand von drei bis vier Jahren, die alles auf einmal ausliefern wollten. Features verzögerten sich, weil sie noch nicht fertig waren, was das gesamte Release verzögerte, was wiederum den Druck erhöhte, alles ins nächste Release zu packen, das dann ebenfalls verzögert wurde. Java 9 brauchte über drei Jahre, auch wegen der Komplexität des Modulsystems.
Mit Sechs-Monate-Releases kommen Features, wenn sie fertig sind. Pattern Matching wurde schrittweise über Java 16 bis 24 ausgeliefert, wobei jedes Release ein weiteres Teil hinzufügte. Virtual Threads durchliefen zwei Preview-Releases, bevor sie final wurden. Dieser inkrementelle Ansatz erlaubt es dem Java-Team, reale Rückmeldungen zu sammeln und Designs anzupassen, bevor dauerhafte API-Entscheidungen getroffen werden.
Die Lehre geht über Java hinaus: Kleine, häufige Releases summieren sich zu tiefgreifendem Wandel. Jedes einzelne Java-Release seit 17 war für sich betrachtet bescheiden. Zusammen haben sie die Sprache neu erfunden. Wenn du Java vor fünf Jahren wegen der Ausführlichkeit verworfen hast, probier es noch einmal. Du könntest überrascht sein, was du findest.


