Skip to content

Latest commit

 

History

History
417 lines (323 loc) · 11.2 KB

Notes.md

File metadata and controls

417 lines (323 loc) · 11.2 KB

Java 26

Install Java 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.vector

New features

JEP 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 Application

JVMLS 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 10000

JEP 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 NullPointerException

JEP_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 -> {}
}

Add Dark Theme to API Documentation

https://docs.oracle.com/en/java/javase/26/docs/api/index.html

Resources