Erstellt von Frank Rahn
Die Programmiersprache Java entwickelt sich von Version zu Version weiter. In dieser Präsentation werden die wichtigsten Änderungen aus Sicht eines Entwicklers dargestellt.
Es werden Programmierkenntnisse ab der Java Version 7 vorausgesetzt.
Seit 1992 arbeite ich als freiberuflicher unabhängiger Softwarearchitekt und -consultant.
Seitdem beschäftige ich mich mit dem Entwurf und der Realisierung von Anwendungen und verfüge über umfangreiche Erfahrungen in der Integration von Anwendungen.
Release im März 2014
Ist eine LTS-Version
Support Ende im Januar 2019
package java8;
import java.util.List;
public class Lambda {
public void printList1(List<String> strings) {
strings.forEach(
s -> {
System.out.println(s);
}
);
}
public void printList2(List<String> strings) {
strings.forEach(System.out::println);
}
}
package java8;
public interface DefaultStaticMethod {
void abstractMethod();
default void defaultMethod() {
System.out.println("Call defaultMethod");
abstractMethod();
}
static void staticMethod() {
System.out.println("Call staticMethod");
}
}
package java8;
public class DefaultStaticMethodImpl implements DefaultStaticMethod {
@Override
public void abstractMethod() {
System.out.println("Call abstractMethod");
DefaultStaticMethod.staticMethod();
}
}
package java8;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
public class Streams {
public void process() {
Stream<String> streamEmpty = Stream.empty();
Stream<String> streamOfArray = Stream.of("a", "b", "c");
Stream<String> streamIterated =
Stream.iterate(40, n -> n + 2).limit(20);
IntStream intStream = IntStream.range(1, 3);
LongStream longStream = LongStream.rangeClosed(1, 3);
}
}
package java8;
import java.util.List;
public class BulkOperation {
public long process(List<String> strings) {
return
strings
.stream()
.filter(s -> s.startsWith("test"))
.sorted()
.map(String::length)
.count();
}
}
package java8;
import java.time.LocalDate;
import java.time.LocalDateTime;
public class DateTime {
public boolean process() {
LocalDate birthday = LocalDate.of(1967, 5, 5);
LocalTime now = LocalTime.now();
LocalDateTime today = LocalDateTime.now();
LocalDateTime tomorrow = today.plusDays(1);
return tomorrow.isAfter(today);
}
}
Release im September 2017
Support Ende im März 2018
module changelog.java.version.java8 {
exports java8;
}
module changelog.java.version.java9 {
requires changelog.java.version.java8;
exports java9;
}
$ jshell
| Welcome to JShell -- Version 11.0.8
| For an introduction type: /help intro
jshell> 1 + 2
$1 ==> 3
jshell> System.out.println($1)
3
jshell> var num = $1
num ==> 3
jshell> /exit
| Goodbye
$
$ jshell
| Welcome to JShell -- Version 11.0.8
| For an introduction type: /help intro
jshell> Set.of("a", "b", "c")
$1 ==> [a, b, c]
jshell> $1.getClass()
$2 ==> class java.util.ImmutableCollections$SetN
jshell> List.of("a", "b", "c")
$3 ==> [a, b, c]
jshell> $3.getClass()
$4 ==> class java.util.ImmutableCollections$ListN
jshell> Map.ofEntries(
...> Map.entry("key1", new Object()),
...> Map.entry("key2", new Object())
...> )
$5 ==> {key2=java.lang.Object@335eadca, key1=java.lang.Object@210366b4}
jshell> $5.getClass()
$6 ==> class java.util.ImmutableCollections$MapN
package java9;
import java.util.Optional;
import java.util.stream.Stream;
public class Optional9 {
public <T> Stream<T> process(Optional<T> valueOpt) {
T a = valueOpt.or(
() -> null
).get();
valueOpt.ifPresentOrElse(t -> {
System.out.println("not empty");
}, () -> {
System.out.println("empty");
});
return valueOpt.stream();
}
}
package java9;
public interface PrivatMethod {
default void defaultMethod() {
System.out.println("Call defaultMethod");
privateMethod();
}
private void privateMethod() {
System.out.println("Call privateMethod");
privateStaticMethod();
}
private static void privateStaticMethod() {
System.out.println("Call privateStaticMethod");
}
}
Die privaten Methoden können außerhalb des Interfaces nicht aufgerufen werden
$ jshell
| Welcome to JShell -- Version 11.0.8
| For an introduction type: /help intro
jshell> var ph = ProcessHandle.current()
ph ==> 48332
jshell> ph.pid()
$2 ==> 48332
jshell> ph.parent()
$3 ==> Optional[54948]
jshell> ph.children()
$4 ==> java.util.stream.ReferencePipeline$2@185d8b6
jshell> var pi = ph.info()
pi ==> [user: Optional[frank], cmd: /usr/lib/jvm/adoptop ... alTime: Optional[PT0.27S]]
jshell> pi.user()
$6 ==> Optional[frank]
jshell> pi.commandLine()
$7 ==> Optional[/usr/lib/jvm/adoptopenjdk-11-hotspot-amd64/bin/java -agentlib:jdwp=transport=dt_socket,address=localhost:41721 jdk.jshell.execution.RemoteExecutionControl 43829]
jshell> pi.command()
$8 ==> Optional[/usr/lib/jvm/adoptopenjdk-11-hotspot-amd64/bin/java]
Die API basiert auf der Reactive-Stream Initiative
package java9;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Flow;
import java.util.concurrent.Flow.Subscription;
import java.util.concurrent.SubmissionPublisher;
public class Flows implements Flow.Subscriber<String> {
private Subscription subscription;
@Override
public void onSubscribe(Subscription subscription) {
this.subscription = subscription;
System.out.println("Starting ...");
this.subscription.request(1);
}
@Override
public void onNext(String item) {
System.out.println(item);
this.subscription.request(1);
}
@Override
public void onError(Throwable throwable) {
throwable.printStackTrace();
}
static CountDownLatch countDown = new CountDownLatch(1);
@Override
public void onComplete() {
System.out.println("Finished");
countDown.countDown();
}
public static void main(String[] args) throws InterruptedException {
SubmissionPublisher<String> publisher = new SubmissionPublisher<>();
publisher.subscribe(new Flows());
List.of("a", "b", "c", "d").forEach(publisher::submit);
publisher.close();
countDown.await();
}
}
$ jshell
| Welcome to JShell -- Version 11.0.8
| For an introduction type: /help intro
jshell> Stream.of(10, 42, 4711)
$1 ==> java.util.stream.ReferencePipeline$Head@548e7350
jshell> $1.dropWhile(i -> i < 50).forEach(System.out::println)
4711
jshell> Stream.of(10, 42, 4711)
$3 ==> java.util.stream.ReferencePipeline$Head@5a8806ef
jshell> $3.takeWhile(i -> i < 50).forEach(System.out::println)
10
42
jshell> IntStream.iterate(1, i -> i < 4711, i -> i*42).forEach(System.out::println)
1
42
1764
jshell> Stream.ofNullable(null)
$5 ==> java.util.stream.ReferencePipeline$Head@7f13d6e
jshell> $5.forEach(System.out::println)
jshell> Stream.ofNullable("Frank")
$7 ==> java.util.stream.ReferencePipeline$Head@10bbd20a
jshell> $7.forEach(System.out::println)
Frank
public class VariableHandles {
private int testVariable = 4711;
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
VariableHandles variableHandles = new VariableHandles();
// Hole ein Handle für das private Attribut dieser Klasse
VarHandle varHandleForTestVariable =
MethodHandles
.privateLookupIn(VariableHandles.class, MethodHandles.lookup())
.findVarHandle(VariableHandles.class, "testVariable", int.class);
// Lesender Zugriff
Object testVariableValue =
varHandleForTestVariable.get(variableHandles);
System.out.println(testVariableValue);
// Schreibender Zugriff
varHandleForTestVariable.set(variableHandles, 15);
testVariableValue =
varHandleForTestVariable.get(variableHandles);
System.out.println(testVariableValue);
}
}
import static java.lang.System.Logger.Level.INFO;
import java.lang.System.Logger;
public class SystemLogger {
private static final Logger LOGGER =
System.getLogger(SystemLogger.class.getName());
public static void main(String[] args) {
if (LOGGER.isLoggable(INFO)) {
LOGGER.log(INFO, "Info in {0}", "If-Abfrage");
}
LOGGER.log(INFO, () -> "Info in %s".formatted("Lambda"));
}
}
Ausgabe:
Okt. 02, 2022 2:44:29 PM java9.SystemLogger main INFORMATION: Info in If-Abfrage Okt. 02, 2022 2:44:29 PM java9.SystemLogger main INFORMATION: Info in Lambda
Release im März 2018
Support Ende im September 2018
package java10;
import java.util.ArrayList;
public class LocalVariable {
public long process() {
var list = new ArrayList<String>();
list.add("Frank");
var stream = list.stream();
return stream.count();
}
}
$ jshell
| Welcome to JShell -- Version 11.0.8
| For an introduction type: /help intro
jshell> Optional.empty().orElseThrow()
| Exception java.util.NoSuchElementException: No value present
| at Optional.orElseThrow (Optional.java:382)
| at (#1:1)
jshell> Optional.of(true).orElseThrow()
$2 ==> true
Release im September 2018
Ist eine LTS-Version
Support Ende im September 2022
jakarta.xml.ws : jakarta.xml.ws-api : 2.3.3
com.sun.xml.ws : jaxws-rt : 2.3.3
jakarta.xml.soap : jakarta.xml.soap-api : 1.4.2
com.sun.xml.messaging.saaj : saaj-impl : 1.5.2
jakarta.xml.bind : jakarta.xml.bind-api : 2.3.3
org.glassfish.jaxb : jaxb-runtime : 2.3.3
jakarta.activation : jakarta.activation-api : 1.2.2
jakarta.transaction : jakarta.transaction-api : 1.3.3
jakarta.persistence : jakarta.persistence-api : 2.2.3
jakarta.validation : jakarta.validation-api : 2.0.2
jakarta.annotation : jakarta.annotation-api : 1.3.5
org.glassfish : jakarta.el : 3.0.3
jakarta.mail : jakarta.mail-api : 1.6.5
jakarta.jms : jakarta.jms-api : 2.0.3
Z. B. Eclipse GlassFisch oder WildFly
$ jshell
| Welcome to JShell -- Version 11.0.8
| For an introduction type: /help intro
jshell> "hello".isBlank()
$1 ==> false
jshell> " ".isBlank()
$2 ==> true
jshell> " Hello World ".strip()
$3 ==> "Hello World"
jshell> " Hello World ".strip()
$4 ==> "Hello World"
jshell> " Hello World ".stripTrailing()
$5 ==> " Hello World"
jshell> " Hello World ".stripLeading()
$6 ==> "Hello World "
jshell> " ".strip()
$7 ==> ""
jshell> "aaa\nbbb\nccc".lines().forEach(System.out::println)
aaa
bbb
ccc
jshell> "-".repeat(20)
$8 ==> "--------------------"
$ jshell
| Welcome to JShell -- Version 11.0.8
| For an introduction type: /help intro
jshell> Character.toString(100)
$1 ==> "d"
jshell> Character.toString(45)
$2 ==> "-"
package java11;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class WriteString {
public String process(URI uri) throws IOException {
Path filePath = Paths.get(uri);
Files.writeString(filePath, "Hello World",
StandardOpenOption.APPEND);
return Files.readString(filePath);
}
}
package java11;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.concurrent.CompletableFuture;
public class HTTPClient {
public CompletableFuture<String> process(String uri) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri)).build();
return client.sendAsync(request,
BodyHandlers.ofString())
.thenApply(HttpResponse::body);
}
}
package java11;
import java.util.function.BiConsumer;
public class LocalVariable {
public BiConsumer<String, String> process() {
return (var x, var y) -> x.startsWith(y);
}
}
Inhalt der Datei hw:
#!/usr/bin/java --source 11
public class HelloWorld {
public static void main (String... args) {
System.out.println("Hello World");
}
}
Ausgabe:
$ ./hw Hello World $
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
Ausgabe:
$ java HelloWorld.java Hello World $
Release im März 2019
Support Ende im September 2019
Release im September 2019
Support Ende im März 2020
Release im März 2020
Support Ende im September 2020
package java14;
import java.time.DayOfWeek;
public class SwitchExpressions {
public int process(DayOfWeek day) {
return switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
default -> {
yield f();
}
};
}
private int f() {
return 4711;
}
}
Release im September 2020
Support Ende im März 2021
package java15;
public class TextBlocks {
private String html = """
<html>
<body>
<p>Hello "world"!</p>
</body>
</html>
""";
private String sql = """
SELECT *
FROM PERSON p \
WHERE p.CITY = '%s' \s
""".formatted("Köln");
}
Release im März 2021
Support Ende im September 2021
instanceof
package java16;
public class Records {
public record Data(String a, Integer b) {}
public static void main(String[] args) {
Data data = new Data("Test", 5711);
Data data2 = new Data("Test", 5711);
System.out.println(data.a());
System.out.println(data.b());
System.out.println(data.toString());
System.out.println(data.hashCode());
System.out.println(data.equals(data2));
}
}
package java16;
public class Records {
public record Data(String a, Integer b) {}
public record NamedTuple<T>(String name, T... values) {}
public static void main(String[] args) {
Data data = new Data("Test", 5711);
Data data2 = new Data("Test", 5711);
System.out.println(data.a());
System.out.println(data.b());
System.out.println(data.toString());
System.out.println(data.hashCode());
System.out.println(data.equals(data2));
var tuple = new NamedTuple<>("Geld", 0.5, 3.14);
System.out.println(tuple);
}
}
package java16;
public class Records {
public record Data(String a, Integer b) {}
public record NamedTuple<T>(String name, T... values) {}
public record Range(int low, int high) {
public Range() {
this(Integer.MIN_VALUE, Integer.MAX_VALUE);
}
public Range {
if (low > high) {
throw new IllegalArgumentException("low greater high");
}
}
public int length() {
return high - low;
}
}
public static void main(String[] args) {
Data data = new Data("Test", 5711);
Data data2 = new Data("Test", 5711);
System.out.println(data.a());
System.out.println(data.b());
System.out.println(data.toString());
System.out.println(data.hashCode());
System.out.println(data.equals(data2));
var tuple = new NamedTuple<>("Geld", 0.5, 3.14);
System.out.println(tuple);
var range = new Range();
System.out.println(range);
System.out.println(range.length());
new Range(2, 1);
}
}
instanceof
package java16;
public class PatternMatching {
public void process(Object obj) {
if (obj instanceof String s) {
System.out.println("obj is String:" + s);
}
return obj instanceof String s && s.isBlank();
}
}
Release im September 2021
Ist eine LTS-Version
Support Ende im September 2030
RandomGenerator)
package java17;
public abstract sealed class Abbildung
permits Kreis, Rechteck {
// ...
}
public final class Kreis extends Abbildung {
// ...
}
public sealed class Rechteck extends Abbildung
permits Quadrat {
// ...
}
public non-sealed class Quadrat extends Rechteck {
// ...
}
Release im März 2022
Support Ende im September 2022
$ jwebserver -b 127.0.0.2 -p 8080 -d /tmp -o verbose
Serving /tmp and subdirectories on 127.0.0.2 port 8080
URL http://127.0.0.2:8080/
var server = SimpleFileServer.createFileServer(
new InetSocketAddress(8080), Path.of("tmp"), OutputLevel.Verbose);
server.start();
package java18;
/**
* ...
*
* {@snippet :
* System.out.println("Hello World!");
* }
*
* {@snippet file="java18/HelloWorld.java" region="output"}
*/
public class HelloWorld {
public static void main(String[] args){
// @start region="output"
System.out.println("Hello World!");
// @end
}
}
Release im September 2022
Support Ende im März 2023
System.out / System.err verwendenHashMap / HashSetMit den folgenden VM-Optionen kann die
Ausgabe wieder auf UTF-8 umgestellt werden:
-Dstdout.encoding=utf8 -Dstderr.encoding=utf8
Es kann auch eine Umgebungsvariable
definiert werden:
_JAVA_OPTIONS="-Dstdout.encoding=utf8 -Dstderr.encoding=utf8"
Bei den HashMap's kann die Anzahl der Elemente
einer Collection angegeben werden:
val map = new HashMap<String, String>(4711);
Allerdings muss beachtet werden, das die Collection mit Default-Load-Factor von 0,75 initialisiert wird.
D. H. die Collection wird tatsächlich mit
0,75 · 4711 = 3533 Elemente
initialisiert. Wird diese
Größe überschritten, wird die Collection verdoppelt und ein Rehash durchgeführt.
Release im März 2023
Support Ende im September 2023
Es wurden 7 Features hinzugefügt, die aber alle entweder Previews oder im Incubator sind.
Release im September 2023
Ist eine LTS-Version
Support Ende im September 2028
package java21;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.SequencedCollection;
public class SequencedCollections {
private final SequencedCollection<Integer> list
= new LinkedList<>();
private final SequencedCollection<Integer> set
= new LinkedHashSet<>();
public void process() {
list.addFirst(4713);
list.addFirst(4711);
list.addLast(4715);
set.addAll(list);
var first = list.get(0);
var last = list.get(list.size() - 1);
first = set.iterator().next();
set.remove(first);
}
package java21;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.SequencedCollection;
public class SequencedCollections {
private final SequencedCollection<Integer> list
= new LinkedList<>();
private final SequencedCollection<Integer> set
= new LinkedHashSet<>();
public void process() {
list.addFirst(4713);
list.addFirst(4711);
list.addLast(4715);
set.addAll(list);
var first = list.getFirst();
var last = list.getLast();
first = set.removeFirst();
}
}
public class PatternMatching {
private final String pi = String.valueOf(Math.PI);
private String formatterPatternSwitch(Object obj) {
return switch (obj) {
case null -> "Object is null";
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> String.format("Object %s", obj);
};
}
public void process() {
println(formatterPatternSwitch(null));
println(formatterPatternSwitch(Integer.valueOf("3")));
println(formatterPatternSwitch(Long.valueOf("3")));
println(formatterPatternSwitch(Double.valueOf(pi)));
println(formatterPatternSwitch(pi));
println(formatterPatternSwitch(new BigDecimal(pi, UNLIMITED)));
}
private void println(Object obj) {
System.out.println(obj);
}
}
public class PatternMatching {
private void guardedCaseLabel(String text) {
switch (text) {
case null -> println("Null");
case "Foo", "Bar" -> println("Ok");
case String s when s.equals("yes") -> println("True");
case String s when s.equals("no") -> println("False");
case String s -> println(s);
}
}
public void process() {
guardedCaseLabel(null);
guardedCaseLabel("Foo");
guardedCaseLabel("Bar");
guardedCaseLabel("yes");
guardedCaseLabel("no");
guardedCaseLabel("Hä?");
}
private void println(Object obj) {
System.out.println(obj);
}
}
public class PatternMatching {
private void nullAndDefault(String s) {
switch (s) {
case "Text" -> println("Text");
case null, default -> println(s);
}
}
public void process() {
nullAndDefault(null);
nullAndDefault("Text");
nullAndDefault("Blubber");
}
private void println(Object obj) {
System.out.println(obj);
}
}
Dekonstruierung von Records für Mustervergleiche
public class RecordPatterns {
record X(Integer i) {}
record Value<T1, T2>(T1 first, T2 second) {}
private void guardedPatterns(Object obj) {
if (obj instanceof X(Integer i)) {
System.out.print("i=" + i + ", ");
}
switch (obj) {
case null -> println("Null");
case X(var x) when x > 4 -> println("x > 4");
case X(var x) -> println(x.intValue());
case Value(String a, var b) -> println(a + ": " + b);
default -> println("Hä?");
}
}
public void process() {
guardedPatterns(null);
guardedPatterns(new X(0));
guardedPatterns(new X(5));
guardedPatterns(new Value<>("Test", 5));
guardedPatterns(new Value<>(0, 5));
guardedPatterns(new Object());
}
private void println(Object obj) {
System.out.println(obj);
}
}
public sealed interface TypePatterns
permits TypePatterns.Enum, TypePatterns.Clazz {
enum Enum implements TypePatterns {A, B}
final class Clazz implements TypePatterns {}
private static void typePatterns(TypePatterns typePatterns) {
switch (typePatterns) {
case Enum.A -> System.out.println("Enum.A");
case Enum.B -> System.out.println("Enum.B");
case Clazz z -> System.out.println("Clazz");
}
}
static void process() {
typePatterns(Enum.A);
typePatterns(Enum.B);
typePatterns(new Clazz());
typePatterns(null);
}
}
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:233)
at java21.TypePatterns.typePatterns(TypePatterns.java:8)
at java21.TypePatterns.process(TypePatterns.java:19)
at ...
public class VirtualThreads {
private static void println(String prefix) {
System.out.printf(
"Thread %s: %d %s %n",
prefix,
System.currentTimeMillis(),
Thread.currentThread());
}
private void sleep() {
println("Started");
try {
Thread.sleep(Duration.ofSeconds(2));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
println("Ended ");
}
public void process() {
var t1 = Thread.ofVirtual().unstarted(this::sleep);
t1.start();
var t2 = Thread.ofPlatform().start(this::sleep);
try (var executor =
Executors.newVirtualThreadPerTaskExecutor()) {
var future = executor.submit(this::sleep);
t1.join();
t2.join();
future.resultNow();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
Thread Started: 1696360867033 VirtualThread[#32]
/runnable@ForkJoinPool-1-worker-1
Thread Started: 1696360867033 Thread[#34,Thread-0,5,main]
Thread Started: 1696360867034 VirtualThread[#35]
/runnable@ForkJoinPool-1-worker-2
Thread Ended : 1696360869047 Thread[#34,Thread-0,5,main]
Thread Ended : 1696360869047 VirtualThread[#32]
/runnable@ForkJoinPool-1-worker-2
Thread Ended : 1696360869047 VirtualThread[#35]
/runnable@ForkJoinPool-1-worker-4
Release im März 2024
Support Ende im September 2024
public class UnnamedVariablesPatterns {
static void unnamedVariables() {
try {
System.out.print("do sleeping");
Thread.sleep(1000);
System.out.println("... done");
} catch (InterruptedException _) {}
var optional = Optional.of("Yes");
System.out.println(
optional.map(_ -> "Optional: No")
);
var names = List.of("Frank", "Walter", "Martin", "Gerd");
System.out.print("Processing for: ");
for (var _ : names) {
System.out.print('.');
}
System.out.println(" done");
System.out.print("Processing forEach: ");
names.forEach(_ -> System.out.print('.'));
System.out.println(" done");
}
}
public class UnnamedVariablesPatterns {
record Point(int x, int y) {}
record Square(Point p1, int s) {}
static void unnamedPattern() {
Object p = new Point(1, 2);
if (p instanceof Point(int x, _)) {
System.out.println("x=" + x);
}
p = new Square(new Point(1, 2), 3);
if (p instanceof Square(_, int s)) {
System.out.println("s=" + s);
}
}
}
In Java die Fremdfunktion printf
aus der C-Standardbibliothek aufrufen
int printf(const char * format, ...);
public class ForeignFunctionMemoryAPI {
static void process() throws Throwable {
var linker = Linker.nativeLinker();
var stdlib = linker.defaultLookup();
var printfAddress = stdlib.find("printf").orElseThrow();
var printfDescriptor = FunctionDescriptor.of(
ValueLayout.JAVA_INT, ValueLayout.ADDRESS);
var printfHandle = linker.downcallHandle(
printfAddress, printfDescriptor);
try (var heap = Arena.ofConfined()) {
var cString = heap.allocateFrom("Hello World!");
var ret = printfHandle.invoke(cString);
System.out.println(ret);
}
}
}
$ java --enable-native-access=ALL-UNNAMED
ForeignFunctionMemoryAPI.java 12 Hello World!
Da hat die println-Ausgabe von Java
die printf-Ausgabe der C-Standardbibliothek
überholt.
Ausführen von Sourcecode Dateien (in Java 11 eingeführt). Die Datei kann jetzt aus mehreren Dateien bestehen. Bisher musste das gesamte Java-Programm in einer Sourcecode Datei implementiert sein
$ ls HelloWorld.java Helper.java $ java HelloWorld.java Hello World (Multi)
public class HelloWorld {
public static void main(String[] args) {
new Helper().run();
}
}
public class Helper implements Runnable{
@Override
public void run() {
System.out.println("Hello World (Multi)");
}
}
Release im September 2024
Support Ende im März 2025
sun.misc.Unsafe
/// Die Klasse Markdown zeigt die Nutzung von Markdown
/// in Javadoc.
///
/// CSS-Syntax:
/// ```css
/// p {color: red}
/// ```
///
/// @author Frank Rahn
/// @since 25.09.2024
/// @version 1.0
public class Markdown {
/// Die Klasse Markdown zeigt die Nutzung von Markdown
/// in Javadoc.
///
/// CSS-Syntax:
/// ```css
/// p {color: red}
/// ```
///
/// @author Frank Rahn
/// @since 25.09.2024
/// @version 1.0
public class Markdown {
/// Die Klasse Markdown zeigt die Nutzung von Markdown
/// in Javadoc.
///
/// CSS-Syntax:
/// ```css
/// p {color: red}
/// ```
///
/// @author Frank Rahn
/// @since 25.09.2024
/// @version 1.0
public class Markdown {
public class Markdown {
/// Diese Methode zeigt die Nutzung von Markdown an
/// einer Methode.
/// @param argument Ein `int`
/// @return das übergebene Argument als [java.lang.Integer]
/// oder `null`, falls `argument` kleiner 0 ist
public Integer process(int argument) {
return argument < 0 ? null : argument;
}
}
Release im März 2025
Support Ende im September 2025
sun.misc.UnsafeGatherers ermöglichen
komplexe und zutandsbehaftete
Operationen
scan()Eine inkrementelle Akkumulation
System.out.println(
Stream.of(100.0, 70.0, -50.0, 30.0, 75.0, 200.0, 15.0)
.gather(Gatherers.scan(() -> 0.1, Double::sum))
.toList());
[100.1, 170.1, 120.1, 150.1, 225.1, 425.1, 440.1]
fold()Eine geordnete, reduktionsähnliche Operation
System.out.println(
Stream.of(1, 2, 7)
.gather(Gatherers.fold(() -> 1, (x, y) -> x * y))
.toList());
[14]
windowFixed()Eine Gruppierung mit fester Größe
System.out.println(
Stream.of(1, 2, 3, 4, 5, 6, 7)
.gather(Gatherers.windowFixed(3))
.toList());
[[1, 2, 3], [4, 5, 6], [7]]
windowSliding()Eine verschobene Gruppierung mit fester Größe
System.out.println(
Stream.of("1", "2", "3", "4", "5", "6", "7")
.gather(Gatherers.windowSliding(3))
.toList());
[[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7]]
mapConcurrent()In fünf Threads parallel
die Eingabezahlen quadrieren
System.out.println(
Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.gather(Gatherers.mapConcurrent(5, x -> x * x))
.toList());
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
System.out.println(
Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)
.gather(Gatherers.windowFixed(3))
.gather(Gatherers.windowFixed(3))
.toList());
System.out.println(
Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)
.gather(Gatherers.windowFixed(3))
.andThen((Gatherers.windowFixed(3))
.toList());
[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]], [[13]]]
Quanten-sichere Schlüsselkapselungsverfahrens
FIPS-203
public class MlKem {
public static final String MESSAGE = "Die geheime Nachricht.";
…
static KeyPair stepFirst() throws GeneralSecurityException {
var keyPairGenerator = KeyPairGenerator.getInstance("ML-KEM");
keyPairGenerator.initialize(NamedParameterSpec.ML_KEM_768); // Default
return keyPairGenerator.generateKeyPair();
}
static Request stepSecond(PublicKey receiverPublicKey) throws GeneralSecurityException {
var message = MESSAGE.getBytes(StandardCharsets.UTF_8);
var kem = KEM.getInstance("ML-KEM");
var encapsulator = kem.newEncapsulator(receiverPublicKey);
var encapsulated = encapsulator.encapsulate();
var sessionKey = encapsulated.key();
var aesKeySpec = new SecretKeySpec(sessionKey.getEncoded(), "AES");
var iv = createInitializationVector();
var gcmSpec = new GCMParameterSpec(128, iv);
var cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, aesKeySpec, gcmSpec);
var encryptedMessage = cipher.doFinal(message);
var data =
ByteBuffer.allocate(iv.length + encryptedMessage.length)
.put(iv)
.put(encryptedMessage)
.array();
return new Request(encapsulated.encapsulation(), data);
}
static String stepThird(PrivateKey receiverPrivateKey, Request request)
throws GeneralSecurityException {
var kem = KEM.getInstance("ML-KEM");
var decapsulator = kem.newDecapsulator(receiverPrivateKey);
var sessionKey = decapsulator.decapsulate(request.keyEncapsulationMessage);
var aesKeySpec = new SecretKeySpec(sessionKey.getEncoded(), "AES");
var gcmSpec = new GCMParameterSpec(128, request.encryptedMessage, 0, IV_LENGTH);
var cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, aesKeySpec, gcmSpec);
var message =
cipher.doFinal(
request.encryptedMessage, IV_LENGTH, request.encryptedMessage.length - IV_LENGTH);
return new String(message, StandardCharsets.UTF_8);
}
static void process() throws GeneralSecurityException {
var receiverKeyPair = stepFirst();
var request = stepSecond(receiverKeyPair.getPublic());
var message = stepThird(receiverKeyPair.getPrivate(), request);
System.out.printf("Received: '%s'%n", message);
}
Quanten-sichere digitale Signaturen FIPS-204
public class MlDsa {
public static final String MESSAGE = "Die Nachricht.";
static KeyPair stepFirst() throws GeneralSecurityException {
var keyPairGenerator = KeyPairGenerator.getInstance("ML-DSA");
keyPairGenerator.initialize(NamedParameterSpec.ML_DSA_65); // Default
return keyPairGenerator.generateKeyPair();
}
static byte[] stepSecond(PrivateKey senderPrivateKey) throws GeneralSecurityException {
var message = MESSAGE.getBytes(StandardCharsets.UTF_8);
var signature = Signature.getInstance("ML-DSA");
signature.initSign(senderPrivateKey);
signature.update(message);
return signature.sign();
}
static Boolean stepThird(PublicKey senderPublicKey, byte[] signatureData)
throws GeneralSecurityException {
var message = MESSAGE.getBytes(StandardCharsets.UTF_8);
var signature = Signature.getInstance("ML-DSA");
signature.initVerify(senderPublicKey);
signature.update(message);
return signature.verify(signatureData);
}
static void process() throws GeneralSecurityException {
var senderkeyPair = stepFirst();
var signatureData = stepSecond(senderkeyPair.getPrivate());
var verified = stepThird(senderkeyPair.getPublic(), signatureData);
System.out.printf("Valid: %s%n", verified);
}
}
sun.misc.UnsafeIn diesem JDK erfolgt die letzte Warnung bei der Nutzung von sun.misc.Unsafe.
Diese Klasse wird durch die folgenden APIs ersetzt:
Diese offizielle API ermöglicht es, Bytecode
Es ersetzt Bibliotheken, wie ASM, Byte Buddy, cglib, Javassist, JaCoCo, Mockito,…
public class ClassFileAPI {
public final static String COMPILER_OUTPUT_WITH_PACKAGE
= "target/classes/java24/";
private static ClassDesc classDesc;
static void process() throws Exception {
classDesc = ClassDesc.of("java24", "User");
ClassFile.of().buildTo(
Path.of(COMPILER_OUTPUT_WITH_PACKAGE
+ classDesc.displayName() + ".class"),
classDesc,
ClassFileAPI::createUserClass
);
…
…
private static void createUserClass(ClassBuilder classBuilder) {
classBuilder
.withFlags(ACC_PUBLIC)
.withField("name", CD_String, ACC_FINAL | ACC_PRIVATE)
.withMethodBody( // Create Constructor
INIT_NAME, // <init>
MethodTypeDesc.of(CD_void, CD_String),
ACC_PUBLIC,
ClassFileAPI::createConstructor)
.withMethodBody( // Override toString()
"toString",
MethodTypeDesc.of(CD_String),
ACC_PUBLIC,
ClassFileAPI::overrideToString)
.with(
SourceFileAttribute.of(
classDesc.displayName() + ".java"))
.with(
RuntimeVisibleAnnotationsAttribute.of(
Annotation.of(
ClassDesc.of(
"javax.annotation.processing",
"Generated"),
AnnotationElement.of(
"value",
AnnotationValue.ofString(
"ClassFileAPI")))));
}
…
Die Erzeugung des Konstrukors und toString()
private static void createConstructor(CodeBuilder codeBuilder) {
codeBuilder
.aload(0) // load this onto stack
.invokespecial(CD_Object, INIT_NAME, MethodTypeDesc.of(CD_void)) // call super constructor
.aload(0) // load this onto stack
.aload(1) // load name onto stack
.putfield(classDesc, "name", CD_String) // Set this.name to the last value on the stack
.return_(); // Return nothing
}
private static void overrideToString(CodeBuilder codeBuilder) {
codeBuilder
.aload(0) // load this onto stack
.getfield(classDesc, "name", CD_String) // Push this.name onto stack
.areturn(); // Return the value on the stack
}
Im Ausgabeverzeichnis (target/classes)
des Compilers wurde die Klasse User
erzeugt.
Dort wird der folgende Befehl ausgeführt,
$ javap -v -c java24/User.class
um den Bytecode der Klasse User
in pseudo Assembler anzuzeigen.
Classfile …/java24/User.class
Last modified … size … bytes
SHA-256 checksum …
Compiled from "User.java"
public class java24.User
minor version: 0
major version: 68
flags: (0x0001) ACC_PUBLIC
this_class: #2 // java24/User
super_class: #8 // java/lang/Object
interfaces: 0, fields: 1, methods: 2, attributes: 2
Constant pool:
…
{
public java24.User(java.lang.String);
descriptor: (Ljava/lang/String;)V
Code:
0: aload_0
1: invokespecial #11 // Method java/lang/Object."<init>":()V
4: aload_0
5: aload_1
6: putfield #13 // Field name:Ljava/lang/String;
9: return
public java.lang.String toString();
descriptor: ()Ljava/lang/String;
Code:
0: aload_0
1: getfield #13 // Field name:Ljava/lang/String;
4: areturn
}
SourceFile: "User.java"
RuntimeVisibleAnnotations:
0: #20(#21=s#22)
javax.annotation.processing.Generated(
value="ClassFileAPI"
)
Release im September 2025
Ist eine LTS-Version
Support Ende im September 2030
ThreadLocal)Das folgende Beispiel hat den Namen CompactSourceAndMain.java
void main () {
IO.println("Hello, World!");
}
Ermöglicht Code vor super(…) und this(…), solange dieser nicht auf das
zu erstellende Objekt verweist
public class Employee extends Person {
public Employee(String name, int age) {
if (age < 0 || age > 100) {
throw new IllegalArgumentException(
"age must be between 0 and 100");
}
super(name, age);
}
}
Klassischer Import:
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Neuer Module-Imort:
import module java.base;
Eine Alternative zu ThreadLocal
public class ScopedValues {
private static final ScopedValue<String> ID
= ScopedValue.newInstance();
private static void process() {
ScopedValue.where(ID, UUID.randomUUID().toString())
.run(
() -> IO.println("Die ID ist " + ID.get())
);
}
}
public class KdfAlgorithmen {
private static void process() throws GeneralSecurityException {
// Das primäre geheime Material
var initialKeyMaterial = "seed-key-material".getBytes();
// Verbessert Sicherheit,
// besonders wenn die Eingabe schwach ist
var salt = "salt".getBytes();
// Verhindert Wiederverwendung des gleichen Schlüssels
// in verschiedenen Kontexten (z. B. authentication)
var info = "encryption".getBytes();
// Eine Parameter-Spezifikation von Typ ExtractExpand erzeugen
var params =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial)
.addSalt(salt)
.thenExpand(info, 32);
// Ein KDF-Objekt für den Algorithmus HKDF-SHA256
// (HMAC-based Key Derivation Function) erzeugen
var hkdf = KDF.getInstance("HKDF-SHA256");
// Den 32-byte AES Schlüssel ermitteln
var key = hkdf.deriveKey("AES", params);
IO.println("Key algorithm: " + key.getAlgorithm());
IO.println("Key length: " + key.getEncoded().length);
}
}
Key algorithm: AES
Key length: 32
Release im März 2026
Support Ende im September 2026
final Felder sind final undRelease im September 2026
Support Ende im März 2027
Dieses Werk ist unter der Creative-Commons-Lizenz vom Typ Namensnennung - Keine Bearbeitungen 4.0 International lizenziert.
Um eine Kopie dieser Lizenz einzusehen, besuchen Sie https://creativecommons.org/licenses/by-nd/4.0/deed.de oder schreiben Sie einen Brief an Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.