无法使用java反射更改静态最终字段?

Evg*_*eev 8 java

我最近偶然发现了使用Java反射更改私有静态最终字段并测试了polygenelubricants的EverythingIsTrue类,工作正常,System.out.format("Everything is %s", false);打印Everything is true确实.但是当我改变代码时

public class EverythingIsTrue {

    public static final boolean FALSE = false;

    static void setFinalStatic(Field field, Object newValue) throws Exception {
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, newValue);
    }

    public static void main(String[] args) throws Exception {
        setFinalStatic(EverythingIsTrue.class.getField("FALSE"), true);
        System.out.format("Everything is %s", FALSE);
    }
}
Run Code Online (Sandbox Code Playgroud)

它打印

Everything is false
Run Code Online (Sandbox Code Playgroud)

有人知道为什么吗?setFinalStatic实际上是否有效?

Pet*_*rey 20

您可以通过使值成为方法调用的结果来避免编译器内联,即使是虚拟调用也是如此.

public class Main {
    // value is not known at compile time, so not inlined
    public static final boolean FLAG = Boolean.parseBoolean("false");

    static void setFinalStatic(Class clazz, String fieldName, Object newValue) throws NoSuchFieldException, IllegalAccessException {
        Field field = clazz.getDeclaredField(fieldName);
        field.setAccessible(true);
        Field modifiers = field.getClass().getDeclaredField("modifiers");
        modifiers.setAccessible(true);
        modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, newValue);
    }

    public static void main(String... args) throws Exception {
        System.out.printf("Everything is %s%n", FLAG);
        setFinalStatic(Main.class, "FLAG", true);
        System.out.printf("Everything is %s%n", FLAG);
    }
}
Run Code Online (Sandbox Code Playgroud)

版画

Everything is false
Everything is true
Run Code Online (Sandbox Code Playgroud)


小智 15

当访问原始静态最终字段时,Java编译器将假定该值是常量并内联该值而不是生成访问该字段的代码.这意味着编译器将FALSE使用值替换对字段的引用false.如果使用反射来访问该字段,您将看到该字段的值实际已更改.

这不适用于非原始字段,因为在编译时无法内联对象引用的值.

  • 实际上,它并不*假设它是恒定的.编译器*分析*表达式以查看它是否是JLS定义的"常量表达式".如果是,则评估它并内联该值.例如,`public static final boolean TRUE ="".isEmpty();`将不会内联,即使你和我明显的值总是为'true`.原因是根据JLS,该表达式不是"常量表达式". (3认同)