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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14102000/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 15:04:42  来源:igfitidea点击:

Cannot change static final field using java reflection?

java

提问by Evgeniy Dorofeev

I recently stumbled upon Change private static final field using Java reflectionand tested polygenelubricants' EverythingIsTrueclass, works fine, System.out.format("Everything is %s", false);prints Everything is trueindeed. But when I change the code as

我最近偶然发现了使用 Java 反射的 Change private static final 字段并测试了 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);
    }
}

it prints

它打印

Everything is false

Does anybody know why? Does setFinalStatic actually work or not?

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

回答by Peter Lawrey

You can avoid compiler inlining by making the value a result of a method call, even a dummy one.

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

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

prints

印刷

Everything is false
Everything is true

回答by Jan B

When accessing primitive static final fields, the Java compiler will assume that the value is a constant and inline the value instead of generating code that accesses the field. This means that the compiler will replace with the reference to the FALSEfield with the value false. If you use reflection to access the field, you will see that the value of the field has actually changed.

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

This will not work for non-primitive fields, as the value of an object reference can not be inlined at compile time.

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