我有一个带有私有静态final字段的类,不幸的是,我需要在运行时更改它。

使用反射我得到这个错误:java.lang.IllegalAccessException:不能设置静态最终布尔字段

有什么方法可以改变这个值吗?

Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);

当前回答

如果你的字段是私有的,你可以这样做:

MyClass myClass= new MyClass();
Field aField= myClass.getClass().getDeclaredField("someField");
aField.setAccessible(true);
aField.set(myClass, "newValueForAString");

和抛出/处理NoSuchFieldException

其他回答

Java语言规范,第17章,第17.5.4节“写保护字段”:

通常,final和static字段不能被修改。 然而,系统。在系统。out和System。Err是静态的最终字段 由于遗留的原因,必须允许方法对其进行更改 系统。开始,系统。setOut和System.setErr。我们提到这些 字段被写入保护,以区别于普通字段 最后一个字段。

来源:http://docs.oracle.com/javase/specs/jls/se7/html/jls - 17. # jls-17.5.4 html

在存在安全管理器的情况下,可以使用AccessController.doPrivileged

从上面接受的答案中取同样的例子:

import java.lang.reflect.*;

public class EverythingIsTrue {
    static void setFinalStatic(Field field, Object newValue) throws Exception {
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");

        // wrapping setAccessible 
        AccessController.doPrivileged(new PrivilegedAction() {
            @Override
            public Object run() {
                modifiersField.setAccessible(true);
                return null;
            }
        });

        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, newValue);
    }

    public static void main(String args[]) throws Exception {      
      setFinalStatic(Boolean.class.getField("FALSE"), true);
      System.out.format("Everything is %s", false); // "Everything is true"
    }
}

在lambda表达式中,AccessController。doPrivileged,可以简化为:

AccessController.doPrivileged((PrivilegedAction) () -> {
    modifiersField.setAccessible(true);
    return null;
});

我还集成了joor库

只使用

      Reflect.on(yourObject).set("finalFieldName", finalFieldValue);

我还修复了一个覆盖的问题,以前的解决方案似乎错过了。 但是,只有在没有其他好的解决方案时,才要小心使用这种方法。

刚刚在一个面试问题上看到了这个问题,如果可能的话,在反射或运行时改变最终变量。 我真的很感兴趣,所以我变成了:

 /**
 * @author Dmitrijs Lobanovskis
 * @since 03/03/2016.
 */
public class SomeClass {

    private final String str;

    SomeClass(){
        this.str = "This is the string that never changes!";
    }

    public String getStr() {
        return str;
    }

    @Override
    public String toString() {
        return "Class name: " + getClass() + " Value: " + getStr();
    }
}

一些带有final String变量的简单类。在主类中 进口java.lang.reflect.Field;

/**
 * @author Dmitrijs Lobanovskis
 * @since 03/03/2016.
 */
public class Main {


    public static void main(String[] args) throws Exception{

        SomeClass someClass = new SomeClass();
        System.out.println(someClass);

        Field field = someClass.getClass().getDeclaredField("str");
        field.setAccessible(true);

        field.set(someClass, "There you are");

        System.out.println(someClass);
    }
}

输出如下:

Class name: class SomeClass Value: This is the string that never changes!
Class name: class SomeClass Value: There you are

Process finished with exit code 0

根据文档 https://docs.oracle.com/javase/tutorial/reflect/member/fieldValues.html

如果赋给一个静态final布尔字段的值在编译时是已知的,那么它就是一个常量。原始或原始字段 字符串类型可以是编译时常量。常量将内联到引用该字段的任何代码中。由于字段在运行时并不实际读取,因此更改它将没有任何影响。

Java语言规范是这样说的:

如果一个场是常数变量 (§4.12.4),然后删除关键字 Final或改变其值将不会 打破与既存状况的兼容性 通过让二进制文件不运行, 但他们不会看到任何新的价值 对于字段的使用,除非他们 重新编译。这是真的,即使 用法本身不是编译时 常量表达式(§15.28)

这里有一个例子:

class Flag {
  static final boolean FLAG = true;
}

class Checker {
  public static void main(String... argv) {
    System.out.println(Flag.FLAG);
  }
}

如果你反编译Checker,你会看到它而不是引用Flag。FLAG,代码只是将一个值1 (true)压入堆栈(指令#3)。

0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3:   iconst_1
4:   invokevirtual   #3; //Method java/io/PrintStream.println:(Z)V
7:   return