JDK26的JEP 500属性

0 阅读1分钟

前言

JDK26的JEP 500属性,为未来"final 真正不可变"做准备,通过深度反射修改 final 字段将触发运行时警告

使用例子

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();
        }
    }
}

输出结果为

image.png

总计

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