Tech-Talk: Java 25 Neuerungen
Michael Faes stellte in diesem Tech-Talk einige Features der neuen Java Version anhand von Code Beispielen vor. Java 25 wurde Mitte September veröffentlicht, kurz vor Semesterstart, und wird bereits im neuen Bachelormodul EIDI (Einstieg in die Informatik) verwendet.
Einsteigerfreundliche Features?
Java 25 fügt einige Features der Sprache hinzu, die einige oft genutzte Konstrukte vereinfachen und verkürzen. Das hilft vor allem, wenn man die Sprache lernt oder generell neu in das Programmieren einsteigt. Über die folgenden Code Snippets hinweg, wird ein etwas komplexeres Hello-World Programm mit neuen Java 25 Features ausgestattet.
import java.util.List;
import java.util.stream.Collectors;
public class HelloWorld {
public static void main(String args[]) {
var greeting = List.of("hello", "java 25!").stream()
.map(s -> s.substring(0, 1).toUpperCase() + s.substring(1))
.collect(Collectors.joining(", "));
System.out.println(greeting);
}
}
Neu kann die main Methode auch ohne public und Parametern geschrieben werden und müssen auch nicht mehr static sein. Wenn die main nicht static ist, wird eine Instanz der Klasse erzeugt über einen parameterlosen Konstruktor, dieser muss vorhanden sein.
import java.util.List;
import java.util.stream.Collectors;
public class HelloWorld {
void main() {
var greeting = List.of("hello", "java 25!").stream()
.map(s -> s.substring(0, 1).toUpperCase() + s.substring(1))
.collect(Collectors.joining(", "));
System.out.println(greeting);
}
}
Das class Konstrukt, um main kann weggelassen werden. Das wird dann ein «Compact Source File» genannt. Eine Klasse um main wird generiert, aber diese ist nicht erreichbar von Java Code, auch nicht via Reflection. In einem Compact Source File sind Definitionen im Top-Level Members von dieser generierten Klasse. Compact Soure Files haben per Definition eine main Funktion im Top-Level.
import java.util.List;
import java.util.stream.Collectors;
// Beispiel
String helloMember = "hello";
void main() {
var greeting = List.of(helloMember, "java 25!").stream()
.map(s -> s.substring(0, 1).toUpperCase() + s.substring(1))
.collect(Collectors.joining(", "));
System.out.println(greeting);
}
Module Imports ermöglichen es weniger Zeilen für Imports zu schreiben. Das Module java.base enthält viele oft verwendeten Klassen. In compact Source Files wird es automatisch importiert.
Zudem gibt es eine neue IO Klasse, welche Konsolen Out- und Input vereinfacht. Interessant ist die Diskussion, ob IO.println nicht direkt statisch als nur println importiert werden soll. Didaktisch macht IO.println mehr Sinn, weil das gleiche Pattern von <Klasse>.<Methode> auch an anderen Orten oft auftritt, zum Beispiel Integer.parseInt oder Math.round.
// Wird automatisch importiert in compact Source Files
// import java.base;
void main() {
var greeting = List.of("hello", "java 25!").stream()
.map(s -> s.substring(0, 1).toUpperCase() + s.substring(1))
.collect(Collectors.joining(", "));
IO.println(greeting);
}
Diese Features zusammen führen dazu, dass beim Einstieg in die Sprache einige komplexe Konstrukte wie Klassen und `static` nicht direkt auftreten. Lernende können sanfter anfangen und müssen weniger unverständlicher Code einfach akzeptieren.
Ahead-of-Time (AOT)
Java Programme haben eine relativ hohe Startup Zeit. Das kommt unteranderem davon, dass bei jeder Ausführung verwendete JAR-Files werden gesucht und Klassen geladen und gelinked. Das Resultat dieses Prozesses verändert sich aber zwischen Ausführungen nur selten und wenig. Diese Feature ermöglicht der JVM die geladenen Klassen gelinked in eine Datei zu cachen. Man kann den Cache bei einer weiteren Durchführung nutzen, um Startup Times zu vermeiden. Das ist primär ein Performance Feature und ist ein Grundbaustein für einen zukünftige AOT-Compiler.
Method Profiling wird auch Ahead-of-Time durchgeführt. Der generierte Cache ist VM spezifisch, wenn er nicht kompatible ist, wird er einfach nicht genutzt. Um den AOT-Cache zu erstellen, wird das -XX:AOTCacheOutput Compiler Flag verwendet. Das ist eine Art Trainingsdurchlauf, alle Klassen, die bei der Ausführung dieses Programms geladen werden, werden gecached.
// HelloWorld.java
void main() {
IO.println("Hello, AOT!");
}
> java -XX:AOTCacheOutput=hello.aot HelloWorld.java
Dieses Programm generierte ein 23MB grossen Cache. -XX:AOTCache wird genutzt, um ein Programm mit dem Cache laufen zu lassen.
> java -XX:AOTCache=hello.aot HelloWorld.java
Ohne Cache lief das Programm für ~400ms mit für ~200ms. Bei einer Spring Applikation mit AsciiDoc, JDBC und einer Template Engine war der Cache 127MB gross. Die Startup Zeit wurde mit Cache von 7.3 auf 5.3 Sekunden reduziert. Wenn etwas geladen werden muss, das sich nicht im Cache befindet, wird es normal geladen. Im Kontrast dazu würde eine VM mit AOT-Compilation, wie GraalVM, abstürzen.
Flexiblere Constructors
In Java 25 wird erlaubt den super Konstruktor nicht als erstes aufzurufen. Vor dem Aufruf kann this allerdings nur genutzt werden, um Werte zu setzen. Alles vor dem super Konstruktor Aufruf wird die Klassenhierarchie hinauf ausgeführt, alles danach hinunter. Das ermöglicht das die Klassen, auf welchen der super Konstruktor arbeitet, bereits valide Daten enthalten.
public class SaverTicket extends Ticket {
private final double priceRatio;
public SaverTicket(int zones, boolean firstClass, double priceRatio) {
// SaverTicket -> Ticket -> ... -> Object
if (priceRatio 1) {
throw new IllegalArgumentException("...");
}
this.priceRatio = priceRatio;
super(zones, firstClass);
// Object -> ... -> Ticket -> SaverTicket
foo();
}
public void foo() { ... }
}
Tech-Talk: Michael Faes (michael.faes@fhnw.ch), Dozent für Informatik, HSI
Blog Beitrag: Janic Berger (janic.berger@fhnw.ch), MSE Student und Assistent am IMVS
Kommentare
Keine Kommentare erfasst zu Tech-Talk: Java 25 Neuerungen