Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 31 additions & 29 deletions src/main/java/org/javademos/init/Java26DemoLoader.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
package org.javademos.init;

import java.util.Map;

import org.javademos.commons.IDemo;
import org.javademos.commons.IDemoLoader;

import org.javademos.java26.jep517.Http3Demo;
import org.javademos.java26.jep522.ReduceSyncDemo;
import org.javademos.java26.jep524.PemEncodingsDemo;
import org.javademos.java26.jep525.StructuredConcurrencyDemo;
import org.javademos.java26.jep526.LazyConstantsDemo;
import org.javademos.java26.jep529.VectorAPIDemo;

/**
* Loads demos for Java 26.
*/
public class Java26DemoLoader implements IDemoLoader {

@Override
public void loadDemos(Map<Integer, IDemo> demos) {
demos.put(517, new Http3Demo());
demos.put(522, new ReduceSyncDemo());
demos.put(524, new PemEncodingsDemo());
demos.put(525, new StructuredConcurrencyDemo());
demos.put(526, new LazyConstantsDemo());
demos.put(529, new VectorAPIDemo());
}
}
package org.javademos.init;

import java.util.Map;

import org.javademos.commons.IDemo;
import org.javademos.commons.IDemoLoader;

import org.javademos.java26.jep500.FinalMeanFinalDemo;
import org.javademos.java26.jep517.Http3Demo;
import org.javademos.java26.jep522.ReduceSyncDemo;
import org.javademos.java26.jep524.PemEncodingsDemo;
import org.javademos.java26.jep525.StructuredConcurrencyDemo;
import org.javademos.java26.jep526.LazyConstantsDemo;
import org.javademos.java26.jep529.VectorAPIDemo;

/**
* Loads demos for Java 26.
*/
public class Java26DemoLoader implements IDemoLoader {

@Override
public void loadDemos(Map<Integer, IDemo> demos) {
demos.put(500, new FinalMeanFinalDemo());
demos.put(517, new Http3Demo());
demos.put(522, new ReduceSyncDemo());
demos.put(524, new PemEncodingsDemo());
demos.put(525, new StructuredConcurrencyDemo());
demos.put(526, new LazyConstantsDemo());
demos.put(529, new VectorAPIDemo());
}
}
94 changes: 94 additions & 0 deletions src/main/java/org/javademos/java26/jep500/FinalMeanFinalDemo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package org.javademos.java26.jep500;

import java.lang.reflect.Field;

import org.javademos.commons.IDemo;

/// Demo for JDK 26 feature JEP 500 - Prepare to Make Final Mean Final.
///
/// Since JDK 5, deep reflection (`Field.setAccessible` + `Field.set`) can reassign `final` fields.
/// JDK 26 starts the process of disallowing that - by default, such a mutation still succeeds,
/// but the JVM prints a warning. A future release will throw `IllegalAccessException` instead.
///
/// Relevant JVM options:
/// - `--enable-final-field-mutation=ALL-UNNAMED` (or `=M1,M2` for named modules)
/// explicitly allows the mutation for the selected code and silences the warning
/// (executable JARs may use the `Enable-Final-Field-Mutation: ALL-UNNAMED` manifest entry)
/// - `--illegal-final-field-mutation=allow|warn|debug|deny` controls what happens
/// on an illegal mutation (`warn` is the default in JDK 26, `deny` will be the future default)
///
/// ### JEP history
/// - JDK 26: [JEP 500 - Prepare to Make Final Mean Final](https://openjdk.org/jeps/500)
///
/// ### Further reading
/// - [Integrity by Default](https://openjdk.org/jeps/8305968)
/// - [Field.set Javadoc](https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/lang/reflect/Field.html#set(java.lang.Object,java.lang.Object))
///
/// @author Alois Sečkár <ellrohir@seznam.cz>
public class FinalMeanFinalDemo implements IDemo {

@Override
public void demo() {
info(500);

// 1) a final field in a normal class
// since JDK 5 deep reflection was allowed to reassign it
// in JDK 26 it still works, but the JVM issues a warning to the standard error stream:
// WARNING: Final field value in ...Holder has been mutated reflectively by class ...FinalMeanFinalDemo ...
// WARNING: Use --enable-final-field-mutation=ALL-UNNAMED to avoid a warning
// (the warning is printed at most once per module, so it may be missing if other code
// in the unnamed module already triggered it before)
// with `--illegal-final-field-mutation=deny`, `Field.set` throws `IllegalAccessException`
var holder = new Holder(100);
System.out.println("Holder value before mutation: " + holder.value);
try {
Field field = Holder.class.getDeclaredField("value");
field.setAccessible(true); // the behavior of `setAccessible` itself is unchanged
field.set(holder, 200); // this is where JDK 26 checks the new restrictions
System.out.println("Holder value after mutation: " + holder.value + " (check stderr for the warning)");
} catch (ReflectiveOperationException ex) {
System.out.println("Final field mutation denied: " + ex);
}

// 2) record components were never mutable via deep reflection (since JDK 16)
// JEP 500 aims to align final fields in normal classes with this behavior
var point = new Point(1, 2);
try {
Field field = Point.class.getDeclaredField("x");
field.setAccessible(true);
field.set(point, 42);
} catch (ReflectiveOperationException ex) {
System.out.println("Record field mutation denied: " + ex.getClass().getSimpleName());
}

// 3) the recommended way - do not mutate, create a new instance with the desired value instead
// (e.g. via constructor, static factory or "wither" method)
var updated = holder.withValue(300);
System.out.println("New Holder created instead of mutation: " + updated.value);

// Notes:
// - to find the code mutating final fields, run with `--illegal-final-field-mutation=debug`
// (prints a stack trace for every mutation) or record a JFR file and look for
// `jdk.FinalFieldMutation` events: `jfr print --events jdk.FinalFieldMutation recording.jfr`
// - serialization libraries should use `sun.reflect.ReflectionFactory` instead of deep reflection
// - `MethodHandles.Lookup.unreflectSetter` on a final field is restricted the same way as `Field.set`
// - mutating final fields via JNI `Set<type>Field` is undefined behavior; `-Xcheck:jni` now warns about it
// - once final means final, the JVM may trust final fields and apply optimizations like constant folding

System.out.println();
}

static class Holder {
final int value;

Holder(int value) {
this.value = value;
}

Holder withValue(int newValue) {
return new Holder(newValue);
}
}

record Point(int x, int y) {}
}
106 changes: 57 additions & 49 deletions src/main/resources/JDK26Info.json
Original file line number Diff line number Diff line change
@@ -1,50 +1,58 @@
[
{
"jep": 517,
"jdk": 26,
"name": "JEP 517 - HTTP/3 for the HTTP Client API",
"dscr": "Update the HTTP Client API to support the HTTP/3 protocol, so that libraries and applications can interact with HTTP/3 servers with minimal code change.",
"link": false,
"code": true
},
{
"jep": 522,
"jdk": 26,
"name":"JEP 522 - G1 GC: Improve Throughput by Reducing Synchronization",
"dscr": "Improve latency and throughput by reducing the amount of synchronization required between application threads and GC threads",
"link": false,
"code": false
},
{
"jep": 524,
"jdk": 26,
"name": "JEP 524 - PEM Encodings of Cryptographic Objects (Second Preview)",
"dscr": "Up-to-date demo in `org.javademos.java27.jep538.PemEncodingsDemo`.",
"link": true,
"code": false
},
{
"jep": 525,
"jdk": 26,
"name": "JEP 525 - Structured Concurrency (Sixth Preview)",
"dscr": "Up-to-date demo in `org.javademos.java27.jep533.StructuredConcurrencyDemo`.",
"link": true,
"code": false
},
{
"jep": 526,
"jdk": 26,
"name": "JEP 526 - Lazy Constants (Preview)",
"dscr": "Up-to-date demo in `org.javademos.java27.jep531.LazyConstantsDemo`.",
"link": true,
"code": false
},
{
"jep": 529,
"jdk": 26,
"name": "JEP 529 - Vector API (Eleventh Incubator)",
"dscr": "Up-to-date demo in `org.javademos.java27.jep537.VectorAPIDemo`.",
"link": true,
"code": false
}
[
{
"jep": 500,
"jdk": 26,
"name": "JEP 500 - Prepare to Make Final Mean Final",
"dscr": "Issue warnings about uses of deep reflection to mutate final fields to prepare for a future release that restricts final field mutation by default.",
"link": false,
"code": true
},
{
"jep": 517,
"jdk": 26,
"name": "JEP 517 - HTTP/3 for the HTTP Client API",
"dscr": "Update the HTTP Client API to support the HTTP/3 protocol, so that libraries and applications can interact with HTTP/3 servers with minimal code change.",
"link": false,
"code": true
},
{
"jep": 522,
"jdk": 26,
"name":"JEP 522 - G1 GC: Improve Throughput by Reducing Synchronization",
"dscr": "Improve latency and throughput by reducing the amount of synchronization required between application threads and GC threads",
"link": false,
"code": false
},
{
"jep": 524,
"jdk": 26,
"name": "JEP 524 - PEM Encodings of Cryptographic Objects (Second Preview)",
"dscr": "Up-to-date demo in `org.javademos.java27.jep538.PemEncodingsDemo`.",
"link": true,
"code": false
},
{
"jep": 525,
"jdk": 26,
"name": "JEP 525 - Structured Concurrency (Sixth Preview)",
"dscr": "Up-to-date demo in `org.javademos.java27.jep533.StructuredConcurrencyDemo`.",
"link": true,
"code": false
},
{
"jep": 526,
"jdk": 26,
"name": "JEP 526 - Lazy Constants (Preview)",
"dscr": "Up-to-date demo in `org.javademos.java27.jep531.LazyConstantsDemo`.",
"link": true,
"code": false
},
{
"jep": 529,
"jdk": 26,
"name": "JEP 529 - Vector API (Eleventh Incubator)",
"dscr": "Up-to-date demo in `org.javademos.java27.jep537.VectorAPIDemo`.",
"link": true,
"code": false
}
]
Loading