- https://en.wikipedia.org/wiki/Java_version_history
- https://openjdk.java.net/projects/jdk/26/
- https://jdk.java.net/26/
curl -s "https://get.sdkman.io" | bash
sdk install java 26-amzn
sdk use java 26-amzn
jshell --enable-preview --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.vectorJEP 500: Prepare to Make Final Mean Final
public class Box {
private final Object value;
public Box(Object value) {
this.value = value;
}
@Override
public String toString() {
return "Box{value=" + value + "}";
}
}Box box = new Box("Rubic's Cube");
IO.println(box);
Field valueField = Box.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set(box, "Magic Wand");
IO.println(box);Java 16 with records:
record Box(String value) {};
var box = new Box("CorunaJUG");
IO.println(box);
Field valueField = Box.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set(box, "VigoJUG");java --illegal-final-field-mutation=deny ApplicationJVMLS 2015 - The Secret History and Tragic Fate of sun.misc.Unsafe
JEP 516: Ahead-of-Time Object Caching with Any GC
# compile
javac InMemAOT.java
# record classes used
java -XX:AOTMode=record -XX:AOTConfiguration=inmem.aotconf InMemAOT 10000
# create the AOT cache with the default GC (G1)
java -XX:AOTMode=create -XX:AOTConfiguration=inmem.aotconf -XX:AOTCache=inmem.aot
# baseline (no AOT, default CDS)
time java InMemAOT 10000
# run using the cache
time java -XX:AOTCache=inmem.aot InMemAOT 10000
# run using the cache
time java -XX:AOTCache=inmem.aot java -XX:+UseZGC InMemAOT 10000
time java -XX:AOTCache=inmem.aot java -XX:+UseCompactObjectHeaders InMemAOT 10000JEP 517: HTTP/3 for the HTTP Client API
import java.net.URI;
import java.net.http.*;
import java.net.http.HttpResponse.BodyHandlers;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://www.galiglobal.com/"))
.build();
HttpResponse<String> response =
client.send(request, BodyHandlers.ofString());
String responseBody = response.body();HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://www.galiglobal.com/"))
.version(HttpClient.Version.HTTP_3)
.build();Defaults to HTTP_2 if 3 not available. Key benefits of HTTP/3:
- Faster on bad networks — packet loss only blocks the affected stream, not all streams
- Quicker connection setup — QUIC combines transport + TLS handshake
- Connection migration — connections survive IP/network changes (great for mobile)
- Better multiplexing — truly independent streams via QUIC
JEP 522: G1 GC: Improve Throughput by Reducing Synchronization
The problem:
- When the Garbage Collector moves an object, it must also adjust all references to that object.
- Therefore, G1 only scans objects within a region (as a reminder: G1 divides the heap into approximately 2,048 regions)
- References from one region to another, however, are stored in a separate, efficient data structure called Card Table using Write Barriers.
- G1 continuously optimizes the Card Table in the background: synchronized -> overhead.
Optimization:
- A second Card Table is introduced.
- The application thread then accesses the previously optimized Card Table, while the optimization thread re-optimizes the Card Table that has become. cumbersome
Results:
- The reduced synchronization overhead can lead to an increase in overall throughput of 5 to 15%.
- The costs for this are minimal: a Card Table occupies 0.2% of the heap – accordingly, with a constant heap size, only 0.2% more native memory is required.
JEP 524: PEM Encodings of Cryptographic Objects (Second Preview)
-----BEGIN CERTIFICATE-----
MIIDtzCCAz2gAwIBAgISBUCeYELtjMmr4FAIqHapebbFMAoGCCqGSM49BAMDMDIx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF
. . .
DBeMde1YpWNXpF9+B/OMKgn7RgXRj5b2QpBCnFsP92T4cK/Nn+xFIjYCMCCx4E79
toSQBlYnNHv0eXnWkI8TmXsU/A6rU4Gxdr9GbGixgRJvkw0C6zjL/lH2Vg==
-----END CERTIFICATE-----
String encryptedPrivateKeyPemEncoded = . . .
String passphrase = . . .
String encryptedPrivateKeyBase64Encoded = encryptedPrivateKeyPemEncoded
.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "")
.replace("-----END ENCRYPTED PRIVATE KEY-----", "")
.replaceAll("[\\r\\n]", "");
Base64.Decoder decoder = Base64.getDecoder();
byte[] encryptedPrivateKeyBytes = decoder.decode(encryptedPrivateKeyBase64Encoded);
EncryptedPrivateKeyInfo encryptedPrivateKeyInfo =
new EncryptedPrivateKeyInfo(encryptedPrivateKeyBytes);
String algorithmName = encryptedPrivateKeyInfo.getAlgName();
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(algorithmName);
PBEKeySpec pbeKeySpec = new PBEKeySpec(passphrase.toCharArray());
Key pbeKey = secretKeyFactory.generateSecret(pbeKeySpec);
Cipher cipher = Cipher.getInstance(algorithmName);
AlgorithmParameters algParams = encryptedPrivateKeyInfo.getAlgParameters();
cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
KeyFactory rsaKeyFactory = KeyFactory.getInstance("RSA");
KeySpec keySpec = encryptedPrivateKeyInfo.getKeySpec(cipher);
PrivateKey privateKey = rsaKeyFactory.generatePrivate(keySpec);PrivateKey privateKey = PEMDecoder.of()
.withDecryption(passphrase.toCharArray())
.decode(encryptedPrivateKeyPemEncoded, PrivateKey.class);JEP 525: Structured Concurrency (Sixth Preview)
Timeout with cancellation:
import java.time.Duration;
import java.util.concurrent.*;
void zzz(long ms){
try{ Thread.sleep(ms);}
catch(InterruptedException e){ Thread.currentThread().interrupt(); throw new RuntimeException(e);}
}
try (var s = StructuredTaskScope.open(
StructuredTaskScope.Joiner.awaitAllSuccessfulOrThrow(),
c -> c.withTimeout(Duration.ofSeconds(1)))) {
s.fork(() -> { zzz(800); return "A"; });
s.fork(() -> { zzz(1500); return "B"; });
var result = s.join();
System.out.println(result);
} catch (StructuredTaskScope.TimeoutException e) {
System.out.println("Timed out");
}import java.time.Duration;
import java.util.concurrent.*;
import java.util.ArrayList;
import java.util.List;
void zzz(long ms) {
try { Thread.sleep(ms); }
catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); }
}
class PartialResultsJoiner<T> implements StructuredTaskScope.Joiner<T, List<T>> {
private final List<T> results = new ArrayList<>();
@Override
public boolean onComplete(StructuredTaskScope.Subtask<T> subtask) {
if (subtask.state() == StructuredTaskScope.Subtask.State.SUCCESS) {
results.add(subtask.get());
}
return false;
}
@Override
public void onTimeout() {
System.out.println("Timeout! Returning partial results: " + results);
}
@Override
public List<T> result() {
return results;
}
}
try (var s = StructuredTaskScope.open(
new PartialResultsJoiner<String>(),
c -> c.withTimeout(Duration.ofSeconds(1)))) {
s.fork(() -> { zzz(800); return "A"; });
s.fork(() -> { zzz(1500); return "B"; });
List<String> results = s.join();
System.out.println("Got: " + results); // Got: [A]
}- The Joiner created by Joiner.allSuccessfulOrThrow() no longer returns a stream of subtasks (which are all successful anyway), but a list of the results of the subtasks.
- The Joiner created by Joiner.allUntil(...) returns a list of subtasks instead of a stream of subtasks.
- The method Joiner.anySuccessfulResultOrThrow() was renamed to anySuccessfulOrThrow() (the word Result was removed, as it is not used in the other Joiner factory methods either).
JEP_526: Lazy Constants (Second Preview)
Stable Values in Java 25.
static volatile String cfgNoStable;
static final Object LOCK = new Object();
static String config2() {
var v = cfgNoStable;
if (v == null) {
synchronized (LOCK) {
if (cfgNoStable == null) {
cfgNoStable = "computed-" + System.currentTimeMillis();
}
v = cfgNoStable;
}
}
return v;
}import java.lang.StableValue;
var cfg = StableValue.<String>of();
String config() {
return cfg.orElseSet(() -> "computed-"+System.currentTimeMillis());
}
config()LazyConstant.of() requires a Supplier argument, not a no-arg factory now:
import java.lang.LazyConstant;
var cfg = LazyConstant.<String>of(() -> "computed-" + System.currentTimeMillis());
cfg.get()private final List<Double> squareRoots = List.ofLazy(100, Math::sqrt);
import java.util.*;
var map = Map.ofLazy(Set.of("a", "b", "c"), k -> k.toUpperCase());
System.out.println(map.get("a"));
System.out.println(map.get("a"));var map = Map.ofLazy(Set.of("a", "b"), k -> null);
map.get("a"); // throws NullPointerExceptionJEP_529: Vector API (Eleventh Incubator)
import jdk.incubator.vector.*;
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
void addVectors(float[] a, float[] b, float[] c) {
int i = 0;
int upperBound = SPECIES.loopBound(a.length);
for (; i < upperBound; i += SPECIES.length()) {
var va = FloatVector.fromArray(SPECIES, a, i);
var vb = FloatVector.fromArray(SPECIES, b, i);
var vc = va.add(vb);
vc.intoArray(c, i);
}
for (; i < a.length; i++) {
c[i] = a[i] + b[i];
}
}Project Valhalla – JEP 401: Value Classes and Objects – reaches the preview stage. This is because the Vector class is intended to be a Value Class from the start – i.e., a class whose objects will manage without identity.
"It's ready when it's ready"
JEP 530: Primitive Types in Patterns, instanceof, and switch (Forth preview)
public Object processValue(Object obj) {
return switch (obj) {
case int i -> "Integer: " + i;
case long l -> "Long: " + l;
case String s -> "String: " + s;
default -> "Unknown: " + obj;
};
}
processValue(2);
processValue("hello CoruñaJUG!");int code = 200; // Only byte, short, char and int
switch (code) {
case 200 -> IO.println("OK");
case 404 -> IO.println("Not Found");
}Smarter in Java 26:
int i1 = 0;
switch (i1) {
case float f -> {}
case 16_777_216 -> {}
default -> {}
}
int i2 = 0;
switch (i2) {
case int _ -> {}
case float _ -> {}
}
byte b = 0;
switch (b) {
case short s -> {}
case 42 -> {}
}https://docs.oracle.com/en/java/javase/26/docs/api/index.html