8

我最近偶然发现了使用 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);
    }
}

它打印

Everything is false

有人知道为什么吗?setFinalStatic 是否真的有效?

4

2 回答 2

23

您可以通过使该值成为方法调用的结果(甚至是虚拟的结果)来避免编译器内联。

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

印刷

Everything is false
Everything is true
于 2012-12-31T13:27:55.923 回答
16

当访问原始静态最终字段时,Java 编译器将假定该值是一个常量并内联该值,而不是生成访问该字段的代码。这意味着编译器将替换为对FALSE具有 value 的字段的引用false。如果你使用反射来访问字段,你会看到字段的值实际上已经改变了。

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

于 2012-12-31T13:18:43.040 回答