Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b6NtPJHrkan4BXhCAM
reflection
Companion code for the ankurm.com post "Java Reflection and MethodHandles in 2026: How They Work
and What They Cost." One module in java-core-examples. Everything the post explains lives in
the post itself (accordions per section); this module is the runnable evidence.
Versions this was built and tested against
| Component | Version | Notes |
|---|---|---|
| JDK | 25.0.5+8 (Temurin, LTS) | All demos and the JMH run. |
| JDK (comparison only) | 26.0.2.1+1 (Temurin) | Used for the JEP 500 final-field warning in output/03. |
| JMH | 1.37 | 3 warmup + 5 measurement iterations, 2 forks. |
| JUnit Jupiter | 5.11.0 | Contract tests - they fail if a documented behaviour changes. |
| Maven | 3.9.11 | |
| Hardware | 2 vCPU Intel Xeon @ 2.30 GHz VM | Shared sandbox: treat ns figures as relative, not absolute. |
Quickstart
export JAVA_HOME=/path/to/jdk-25
mvn package # compiles, runs the contract tests, builds the JMH jar
java src/main/java/com/ankurm/reflection/QuickStart.java
java -cp target/classes com.ankurm.reflection.ReflectionBasicsDemo
java -cp target/classes com.ankurm.reflection.ModuleRulesDemo
java --add-opens java.base/java.lang=ALL-UNNAMED -cp target/classes com.ankurm.reflection.ModuleRulesDemo
java -cp target/classes com.ankurm.reflection.FinalFieldDemo
java -cp target/classes com.ankurm.reflection.MethodHandlesDemo
java -jar target/benchmarks.jar CallBenchmark
scripts/run-all.sh regenerates every file in output/ (set JDK26_HOME to include the JDK 26 runs).
What's in here
| File | What it shows |
|---|---|
Account.java |
The sample class: public API, private method, private/final fields, a throwing method. |
QuickStart.java |
Self-contained: one call by Method, one by MethodHandle. Run with java QuickStart.java. |
ReflectionBasicsDemo.java |
Inspect, construct, invoke; the five exceptions you will meet; extra stack frames. |
ModuleRulesDemo.java |
setAccessible on your class vs. a JDK internal; --add-opens. |
FinalFieldDemo.java |
Final instance / static / record fields; constant-inlining trap; JEP 500 on JDK 26. |
MethodHandlesDemo.java |
invokeExact vs invoke, composition, lookup-time access checks, VarHandle. |
CallBenchmark.java |
JMH: direct vs Method vs MethodHandle vs Field vs VarHandle, static-final vs instance field. |
ReflectionContractTest.java |
Eight tests pinning the contracts the post states. |
Captured output
| File | From |
|---|---|
output/01-basics.txt |
ReflectionBasicsDemo |
output/02-module-rules.txt |
ModuleRulesDemo, without and with --add-opens |
output/03-final-fields.txt |
FinalFieldDemo on JDK 25, JDK 26, and JDK 26 with --illegal-final-field-mutation=deny |
output/04-method-handles.txt |
MethodHandlesDemo |
output/05-call-benchmark.txt |
CallBenchmark (non-deterministic; relative ordering is the result) |
output/06-quickstart.txt |
QuickStart |
Identity-hash suffixes like unnamed module @6b95977 change on every run.