前言
JDK26的JEP 500属性,为未来"final 真正不可变"做准备,通过深度反射修改 final 字段将触发运行时警告
使用例子
csharp
public class FinalFieldDemo {
// 常量表达式初始化: javac 把读取折叠成 ldc "original", 反射改完普通读取仍是旧值
private final String name = "original";
// 非常量表达式初始化: 编译成 getfield, 反射改完普通读取能看到新值
private final String tag = new String("original");
public static void main(String[] args) throws Exception {
FinalFieldDemo demo = new FinalFieldDemo();
System.out.println("=== 修改前 ===");
System.out.println(" 直接读 name = " + demo.name);
System.out.println(" 直接读 tag = " + demo.tag);
// 深反射改 final: JDK 26 默认 warn 模式下写入成功, 仅在 stderr 打印一次 WARNING
Field nameField = FinalFieldDemo.class.getDeclaredField("name");
nameField.setAccessible(true);
Field tagField = FinalFieldDemo.class.getDeclaredField("tag");
tagField.setAccessible(true);
System.out.println("=== 修改后 ===");
System.out.println(" 写 name = " + write(nameField, demo, "hacked-name"));
System.out.println(" 反射读 name = " + nameField.get(demo) + " <- 反射读到的才是真实内存值");
System.out.println(" 直接读 name = " + demo.name + " <- 编译期 ldc 折叠, 看不到运行时改写");
System.out.println(" 写 tag = " + write(tagField, demo, "hacked-tag"));
System.out.println(" 反射读 tag = " + tagField.get(demo));
System.out.println(" 直接读 tag = " + demo.tag + " <- 真实 getfield, 与反射读一致");
}
/**
* 写入 final 字段, 返回写入结果描述。
* warn 模式(默认): 写入成功 + stderr 打印 WARNING;
* deny 模式(未来 JDK 默认): 抛 IllegalAccessException
* "unnamed module @xxx is not allowed to mutate final fields"。
*/
private static String write(Field field, Object target, Object value) {
try {
field.set(target, value);
return "成功";
} catch (IllegalAccessException e) {
return "被拒绝 -> " + e.getMessage();
}
}
}
输出结果为

总计
修改final值会发出警告,但是还是能修改成功