Java 27 and 26: runnable demos and captured output for every JEP, plus version lanes

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01B38FGKKam5SCGgwgduVAh3
This commit is contained in:
2026-09-21 14:31:11 +00:00
committed by Claude
co-authored by Claude Sonnet 5
commit 09b3777efb
137 changed files with 4654 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
Review the attached Java class and convert it to a Java 21 record if it is a pure data carrier. Preserve all validation from the old constructor inside a compact constructor. Flag fields that prevent conversion (e.g. mutable state, inheritance) and explain why.
@@ -0,0 +1 @@
Refactor this Java code to use Java 21 pattern matching for switch. Introduce a sealed interface if the types form a closed hierarchy. Ensure the switch is exhaustive and remove all redundant casts and intermediate variables.
@@ -0,0 +1 @@
Find every usage of Executors.newFixedThreadPool and Executors.newCachedThreadPool in this project. Replace with Executors.newVirtualThreadPerTaskExecutor() where the tasks are I/O-bound. List any cases where platform threads should be kept (CPU-bound work, native frames on the stack, and on Java 21 to 23 also synchronized blocks that block, which pin the carrier thread until Java 24) and justify each decision.
@@ -0,0 +1 @@
Rewrite this code to use Java 21 sequenced collection APIs (getFirst, getLast, reversed) instead of list.get(0), list.get(list.size()-1), and manual reversal. Keep behaviour identical.
@@ -0,0 +1 @@
Identify ThreadLocal usages in this file. Propose a migration to Java 25 ScopedValue, showing before/after code and explaining why ScopedValue is safer under virtual threads and structured concurrency.
@@ -0,0 +1 @@
Refactor this CompletableFuture.allOf pipeline to use StructuredTaskScope. Note that StructuredTaskScope is still a preview API in Java 25, 26 and 27, so the build needs --enable-preview and the code must use the factory-method names of the JDK it targets (Joiner.anySuccessfulResultOrThrow() on 25, anySuccessfulOrThrow() on 26 and 27). Preserve error propagation semantics and show what happens on cancellation. Explain the difference from the original CompletableFuture approach.
+1
View File
@@ -0,0 +1 @@
Here is my pom.xml targeting Java 21. Update it to target Java 25. Bump Spring Boot to the latest 3.5.x or 4.x release (3.4.x is documented only up to Java 24), Hibernate to 7.x (which needs Jakarta Persistence 3.2), Lombok to 1.18.40 or later, JUnit to 6.x, and Mockito to its current 5.x release. List any dependency that does not have a Java 25 compatible version and suggest alternatives.
@@ -0,0 +1 @@
Scan this Java file for usages of SecurityManager, Thread.stop/suspend/resume, Object.finalize, sun.misc.Unsafe memory methods, System.loadLibrary and other JNI loading, and the old java.net.URL constructors. For each finding, say which Java release changed it (SecurityManager: cannot be installed since 24; Thread.suspend/resume: still compile on 21, gone by 23; Thread.stop: throws UnsupportedOperationException on 21 and 25, removed in 26; finalize: still runs, deprecated for removal; Unsafe memory access and JNI loading: run-time warnings from 24) and give a replacement with a before/after code snippet.
@@ -0,0 +1 @@
Review the attached Maven/Gradle build file and list every --enable-preview compiler and runtime argument. For each one, name the preview feature the code actually uses. Remove the flag only if that feature is final in Java 25 (scoped values, JEP 506, are final in 25; StructuredTaskScope is still a preview API in 25, 26 and 27, so a project that uses it must keep the flag). Explain each decision.
@@ -0,0 +1 @@
Apply these Java 25 idioms to this file: records for data carriers, pattern-matching switch for type checks, sequenced collection APIs (getFirst/getLast), and ScopedValue instead of ThreadLocal. Preserve behaviour exactly and add a comment for each change explaining why the new form is preferred.
@@ -0,0 +1 @@
Before I flip my executors to virtual threads on Java 25, audit this class for: ThreadLocal usage, JNI calls or native frames that stay on the stack while blocking (these still pin the carrier), synchronized blocks around I/O (harmless on Java 24 and later, pinning on 21 to 23), and any code that assumes a bounded thread pool. Report risks with line numbers and a severity (blocking/warning/low).
@@ -0,0 +1 @@
Act as a Java 25 upgrade reviewer. Given the attached project files, produce a prioritized checklist of upgrade tasks, group them by risk (blocking / warning / nice-to-have), and estimate effort in developer-hours per group.
+1
View File
@@ -0,0 +1 @@
Act as a Java 25 to Java 27 upgrade reviewer. Scan the attached project for: reflection that writes to final fields (Field.setAccessible followed by Field.set on a final field; Java 26 warns, JEP 500), StructuredTaskScope code that uses the 25 names (Joiner.anySuccessfulResultOrThrow became anySuccessfulOrThrow in 26, and Joiner gained a third type parameter in 27), the -XX:-ZGenerational flag (ignored with a warning on 24 and 25, rejected on 26 and 27), Thread.stop and java.applet (removed in 26), JOL or Unsafe code that assumes a 12-byte object header (compact headers are the default in 27), and container start scripts that rely on the JVM choosing SerialGC in a one-CPU container (G1 is the default everywhere in 27). For each finding give the file, the line, the release that changed it, and the fix.
+15
View File
@@ -0,0 +1,15 @@
/**
* Tiny assertion helper so every demo is self-checking: a claim that stops being true turns the
* run red instead of quietly printing something different. Passing checks are echoed, so the
* transcript shows exactly what was verified.
*/
final class Check {
private Check() {}
static void that(boolean condition, String claim) {
if (!condition) {
throw new AssertionError("CHECK FAILED: " + claim);
}
System.out.println("CHECK ok : " + claim);
}
}
+3
View File
@@ -0,0 +1,3 @@
void main() {
IO.println("compact source file: no class, no static, no String[] args");
}
+27
View File
@@ -0,0 +1,27 @@
/**
* Is finalization "removed by default" in Java 25? JEP 421 deprecated it for removal in 18 and added
* --finalization=disabled to test life without it. This allocates an object with a finalize() method, drops it,
* asks for a GC, and reports whether the finalizer ran. Run by scripts/lanes.sh with and without the flag.
* Chapter: docs/14-lanes-21-to-25.md
*/
public class FinalizerDefault {
static volatile boolean finalized;
@SuppressWarnings({"removal", "deprecation"})
static class Resource {
@Override
protected void finalize() {
finalized = true;
}
}
public static void main(String[] args) throws Exception {
new Resource();
for (int i = 0; i < 20 && !finalized; i++) {
System.gc();
Thread.sleep(50);
}
System.out.println("java.version = " + System.getProperty("java.version"));
System.out.println("finalizer ran: " + finalized);
}
}
+94
View File
@@ -0,0 +1,94 @@
import java.time.Duration;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
/**
* The four Java 21 features from ankurm.com/java-21-to-25-lts-features (records, pattern-matching switch,
* virtual threads, sequenced collections), exactly as the article shows them, compiled once with
* --release 21 and then run unchanged on JDK 21, 25, 26 and 27. Scoped values and structured concurrency are
* separate files because they are NOT final in 21. Chapter: docs/14-lanes-21-to-25.md
*/
public class HubExamples {
// 1. Records ------------------------------------------------------------------------------
public record Customer(Long id, String email, String country) {
public Customer {
Objects.requireNonNull(email, "email must not be null");
if (!email.contains("@")) throw new IllegalArgumentException("Invalid email");
}
}
// 2. Pattern matching for switch ----------------------------------------------------------
sealed interface Shape permits Circle, Square, Triangle {}
record Circle(double r) implements Shape {}
record Square(double side) implements Shape {}
record Triangle(double base, double height) implements Shape {}
static double area(Shape s) {
return switch (s) {
case Circle(double r) -> Math.PI * r * r;
case Square(double side) -> side * side;
case Triangle(double b, double h) -> 0.5 * b * h;
};
}
static String classify(Shape s) {
return switch (s) {
case Circle c when c.r() > 100 -> "large circle";
case Circle c -> "small circle";
case Square sq -> "square";
case Triangle t -> "triangle";
};
}
public static void main(String[] args) throws Exception {
System.out.println("java.version = " + System.getProperty("java.version"));
Customer c = new Customer(1L, "[email protected]", "IN");
Check.that(c.email().equals("[email protected]"), "record accessor: c.email()");
Check.that(c.toString().equals("Customer[id=1, [email protected], country=IN]"), "record toString: " + c);
try {
new Customer(2L, "not-an-email", "IN");
Check.that(false, "compact constructor rejects an email without @");
} catch (IllegalArgumentException expected) {
Check.that(true, "compact constructor rejects an email without @: " + expected.getMessage());
}
Check.that(Math.abs(area(new Square(3)) - 9.0) < 1e-9, "area(Square(3)) == 9.0");
Check.that(Math.abs(area(new Triangle(4, 5)) - 10.0) < 1e-9, "area(Triangle(4, 5)) == 10.0");
Check.that(classify(new Circle(101)).equals("large circle"), "guard pattern: Circle(101) is a large circle");
Check.that(classify(new Circle(1)).equals("small circle"), "guard pattern: Circle(1) is a small circle");
// 3. Virtual threads: 10,000 tasks that each block for 100 ms. Serial would take 1,000 s.
AtomicInteger done = new AtomicInteger();
long t0 = System.nanoTime();
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i ->
executor.submit(() -> {
Thread.sleep(Duration.ofMillis(100));
return done.incrementAndGet();
}));
}
long ms = (System.nanoTime() - t0) / 1_000_000;
Check.that(done.get() == 10_000, "all 10,000 virtual-thread tasks completed before close() returned");
Check.that(ms < 30_000, "and it took seconds, not the 1,000 s a serial loop needs (exact time is machine dependent, not printed)");
Thread vt = Thread.ofVirtual().name("my-vthread").start(() -> { });
vt.join();
Check.that(vt.isVirtual() && vt.getName().equals("my-vthread"), "Thread.ofVirtual().name(..).start(..) gives a virtual thread");
// 4. Sequenced collections
List<String> names = new ArrayList<>(List.of("alex", "brian", "chris"));
Check.that(names.getFirst().equals("alex") && names.getLast().equals("chris"), "List.getFirst() / getLast()");
Check.that(names.reversed().toString().equals("[chris, brian, alex]"), "List.reversed(): " + names.reversed());
names.add("dana");
Check.that(names.reversed().getFirst().equals("dana"), "reversed() is a view, not a copy: it sees the element added afterwards");
LinkedHashSet<Integer> nums = new LinkedHashSet<>(List.of(3, 1, 4, 1, 5));
Check.that(nums.getFirst() == 3 && nums.getLast() == 5, "LinkedHashSet.getFirst() == 3, getLast() == 5");
}
}
+13
View File
@@ -0,0 +1,13 @@
/**
* "JNI warnings on stderr by default": JEP 472 (Java 24). Loading a native library is a restricted operation.
* The library is the JDK's own libnet, so no compiler or extra file is needed. Run with and without
* --enable-native-access=ALL-UNNAMED by scripts/lanes.sh.
* Chapter: docs/14-lanes-21-to-25.md
*/
public class JniWarning {
public static void main(String[] args) {
System.out.println("java.version = " + System.getProperty("java.version"));
System.loadLibrary("net");
System.out.println("System.loadLibrary(\"net\") returned");
}
}
+29
View File
@@ -0,0 +1,29 @@
import java.util.ArrayList;
import java.util.List;
/**
* Virtual threads and synchronized: until JEP 491 (Java 24) a virtual thread that blocked inside a synchronized
* block kept its carrier thread (was "pinned"). With ONE carrier, four virtual threads that each sleep 200 ms inside
* synchronized on their own lock take about 800 ms if pinned (one at a time) and about 200 ms if not (all at once).
* Run by scripts/lanes.sh with -Djdk.virtualThreadScheduler.parallelism=1 -Djdk.virtualThreadScheduler.maxPoolSize=1
* Chapter: docs/14-lanes-21-to-25.md
*/
public class PinningDemo {
public static void main(String[] args) throws Exception {
System.out.println("java.version = " + System.getProperty("java.version"));
List<Thread> threads = new ArrayList<>();
long t0 = System.nanoTime();
for (int i = 0; i < 4; i++) {
Object lock = new Object();
threads.add(Thread.ofVirtual().start(() -> {
synchronized (lock) {
try { Thread.sleep(200); } catch (InterruptedException ignored) { }
}
}));
}
for (Thread t : threads) t.join();
long ms = (System.nanoTime() - t0) / 1_000_000;
System.out.println("4 virtual threads, 1 carrier, sleep(200) inside synchronized: "
+ (ms >= 700 ? "SERIALISED (pinned): about 4 x 200 ms" : "OVERLAPPED (not pinned): about 200 ms"));
}
}
+28
View File
@@ -0,0 +1,28 @@
import java.util.NoSuchElementException;
/**
* Scoped values (JEP 506): final in Java 25, a preview API in 21. The code is the example from
* ankurm.com/java-21-to-25-lts-features. Compiles WITHOUT --enable-preview on 25, 26 and 27; on 21 javac refuses.
* Chapter: docs/14-lanes-21-to-25.md
*/
public class ScopedValueDemo {
static final ScopedValue<String> CURRENT_USER = ScopedValue.newInstance();
public static void main(String[] args) {
System.out.println("java.version = " + System.getProperty("java.version"));
ScopedValue.where(CURRENT_USER, "ankur").run(() -> {
Check.that(CURRENT_USER.get().equals("ankur"), "inside the scope: CURRENT_USER.get() == ankur");
callDeepMethod();
});
try {
CURRENT_USER.get();
Check.that(false, "outside the scope get() should throw");
} catch (NoSuchElementException expected) {
Check.that(true, "outside the scope: CURRENT_USER.get() throws NoSuchElementException");
}
}
static void callDeepMethod() {
Check.that(CURRENT_USER.get().equals("ankur"), "a method that was never passed the value still sees it");
}
}
+17
View File
@@ -0,0 +1,17 @@
/**
* "System.setSecurityManager() now throws UnsupportedOperationException": JEP 486 (Java 24) permanently disabled
* the Security Manager. This calls it with a non-null manager and prints exactly what happens.
* Chapter: docs/14-lanes-21-to-25.md
*/
public class SecurityManagerGone {
@SuppressWarnings({"removal", "deprecation"})
public static void main(String[] args) {
System.out.println("java.version = " + System.getProperty("java.version"));
try {
System.setSecurityManager(new SecurityManager());
System.out.println("setSecurityManager: accepted");
} catch (UnsupportedOperationException e) {
System.out.println("setSecurityManager: UnsupportedOperationException: " + e.getMessage());
}
}
}
+37
View File
@@ -0,0 +1,37 @@
import java.util.concurrent.StructuredTaskScope;
/**
* The structured concurrency example from ankurm.com/java-21-to-25-lts-features. JEP 505 in Java 25 is the FIFTH
* PREVIEW, not a final feature: without --enable-preview javac refuses, on 25, 26 and 27 alike.
* Chapter: docs/14-lanes-21-to-25.md
*/
public class StructuredHub {
record Dashboard(String user, String order) {}
static String fetchUser(int id) throws InterruptedException { Thread.sleep(20); return "user-" + id; }
static String fetchOrder(int id) throws InterruptedException { Thread.sleep(30); return "order-for-" + id; }
static Dashboard fanOut(int userId) throws Exception {
try (var scope = StructuredTaskScope.open()) {
var user = scope.fork(() -> fetchUser(userId));
var order = scope.fork(() -> fetchOrder(userId));
scope.join();
return new Dashboard(user.get(), order.get());
}
}
static String firstSuccess() throws Exception {
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
scope.fork(() -> { Thread.sleep(200); return "slow"; });
scope.fork(() -> "fast");
return scope.join();
}
}
public static void main(String[] args) throws Exception {
System.out.println("java.version = " + System.getProperty("java.version"));
Dashboard d = fanOut(7);
Check.that(d.equals(new Dashboard("user-7", "order-for-7")), "fan-out: both results joined: " + d);
Check.that(firstSuccess().equals("fast"), "anySuccessfulResultOrThrow(): the first result wins and the slow subtask is cancelled");
}
}
+21
View File
@@ -0,0 +1,21 @@
import java.util.concurrent.StructuredTaskScope;
/**
* The same "first result wins" example as StructuredHub, with the factory method under its Java 26 / 27 name:
* anySuccessfulResultOrThrow() (25) became anySuccessfulOrThrow() (26 and 27). Still a preview API.
* Chapter: docs/14-lanes-21-to-25.md
*/
public class StructuredHubNew {
static String firstSuccess() throws Exception {
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulOrThrow())) {
scope.fork(() -> { Thread.sleep(200); return "slow"; });
scope.fork(() -> "fast");
return scope.join();
}
}
public static void main(String[] args) throws Exception {
System.out.println("java.version = " + System.getProperty("java.version"));
Check.that(firstSuccess().equals("fast"), "anySuccessfulOrThrow(): the first result wins");
}
}
+24
View File
@@ -0,0 +1,24 @@
/**
* Thread.stop() on 25: still compiles (with a deprecation-for-removal warning) but has thrown
* UnsupportedOperationException since Java 20. It is REMOVED from the API in Java 26 (see recap26/src/broken/ThreadStopGone.java,
* docs/output/73-removed-in-26.txt). Thread.suspend() and resume() are checked the same way.
* Chapter: docs/14-lanes-21-to-25.md
*/
public class ThreadStopRuntime {
@SuppressWarnings({"removal", "deprecation"})
public static void main(String[] args) throws Exception {
System.out.println("java.version = " + System.getProperty("java.version"));
Thread t = new Thread(() -> {
try { Thread.sleep(1000); } catch (InterruptedException ignored) { }
});
t.start();
try {
t.stop();
System.out.println("Thread.stop(): accepted");
} catch (UnsupportedOperationException e) {
System.out.println("Thread.stop(): UnsupportedOperationException");
}
t.interrupt();
t.join();
}
}
+11
View File
@@ -0,0 +1,11 @@
/**
* Compile-only probe: do Thread.suspend() and Thread.resume() still exist? Compiled by scripts/lanes.sh with each JDK's own javac.
* Chapter: docs/14-lanes-21-to-25.md
*/
public class ThreadSuspendGone {
@SuppressWarnings({"removal", "deprecation"})
static void probe(Thread t) {
t.suspend();
t.resume();
}
}
+21
View File
@@ -0,0 +1,21 @@
/**
* "Unnamed patterns and unnamed classes are also final in 25" is only half true. Unnamed variables and patterns (the
* underscore) were finalised in Java 22 (JEP 456). What became final in 25 is compact source files and instance main
* methods (JEP 512), the successor to "unnamed classes". This file checks the underscore half.
* Chapter: docs/14-lanes-21-to-25.md
*/
public class UnnamedVariables {
record Point(int x, int y) {}
public static void main(String[] args) {
Object o = new Point(3, 4);
if (o instanceof Point(int x, _)) {
System.out.println("matched a Point, x = " + x + ", y ignored with _");
}
try {
Integer.parseInt("nope");
} catch (NumberFormatException _) {
System.out.println("caught NumberFormatException without naming it");
}
}
}
+21
View File
@@ -0,0 +1,21 @@
import java.lang.reflect.Field;
import sun.misc.Unsafe;
/**
* "sun.misc.Unsafe memory methods deprecated for removal": JEP 471 (23) deprecated them, JEP 498 (24) makes the first
* call print a warning at run time. This allocates and frees 8 bytes off-heap and shows what the JVM says.
* Chapter: docs/14-lanes-21-to-25.md
*/
public class UnsafeWarning {
@SuppressWarnings("removal")
public static void main(String[] args) throws Exception {
System.out.println("java.version = " + System.getProperty("java.version"));
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe u = (Unsafe) f.get(null);
long addr = u.allocateMemory(8);
u.putLong(addr, 42L);
System.out.println("read back: " + u.getLong(addr));
u.freeMemory(addr);
}
}