我有一个带有 private static final
字段的类,不幸的是,我需要在运行时更改它。
使用反射我得到这个错误:java.lang.IllegalAccessException: Can not set static final boolean field
有什么办法可以改变值吗?
Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);
最佳答案
假设没有 SecurityManager
阻止您这样做,您可以使用 setAccessible
绕过 private
并重置修饰符以摆脱final
,实际上是修改了一个private static final
字段。
这是一个例子:
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");
modifiersField.setAccessible(true);
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"
}
}
假设没有SecurityException
被抛出,上面的代码打印出"Everything is true"
。
这里实际做了如下:
main
中的原始boolean
值true
和false
被自动装箱为引用类型Boolean
"常量"Boolean.TRUE
和Boolean.FALSE
- 反射用于改变
public static final Boolean.FALSE
引用Boolean.TRUE
所引用的 - 因此,随后每当
false
自动装箱为Boolean.FALSE
时,它引用的Boolean
与所引用的相同byBoolean.TRUE
- 所有以前的
"false"
现在都是"true"
Boolean
相关问题
- Using reflection to change
static final File.separatorChar
for unit testing - How to limit setAccessible to only “legitimate” uses?
- 有弄乱
Integer
的缓存、改变String
等的例子
- 有弄乱
注意事项
每当您执行此类操作时都应格外小心。它可能不起作用,因为可能存在 SecurityManager
,但即使它不存在,根据使用模式,它也可能起作用,也可能不起作用。
JLS 17.5.3 Subsequent Modification of Final Fields
In some cases, such as deserialization, the system will need to change the
final
fields of an object after construction.final
fields can be changed via reflection and other implementation dependent means. The only pattern in which this has reasonable semantics is one in which an object is constructed and then thefinal
fields of the object are updated. The object should not be made visible to other threads, nor should thefinal
fields be read, until all updates to thefinal
fields of the object are complete. Freezes of afinal
field occur both at the end of the constructor in which thefinal
field is set, and immediately after each modification of afinal
field via reflection or other special mechanism.Even then, there are a number of complications. If a
final
field is initialized to a compile-time constant in the field declaration, changes to thefinal
field may not be observed, since uses of thatfinal
field are replaced at compile time with the compile-time constant.Another problem is that the specification allows aggressive optimization of
final
fields. Within a thread, it is permissible to reorder reads of afinal
field with those modifications of a final field that do not take place in the constructor.
另见
- JLS 15.28 Constant Expression
- 这种技术不太可能与原始
private static final boolean
一起使用,因为它可以作为编译时常量内联,因此"new"值可能无法观察到
- 这种技术不太可能与原始
附录:按位操作
基本上,
field.getModifiers() & ~Modifier.FINAL
关闭 field.getModifiers()
中与 Modifier.FINAL
对应的位。 &
是按位与,~
是按位补码。
另见
记住常量表达式
仍然无法解决这个问题?像我一样陷入抑郁症吗?你的代码是这样的吗?
public class A {
private final String myVar = "Some Value";
}
阅读对此答案的评论,特别是@Pshemo 的评论,它提醒我 Constant Expressions处理方式不同,因此不可能对其进行修改。因此,您需要将代码更改为如下所示:
public class A {
private final String myVar;
private A() {
myVar = "Some Value";
}
}
如果你不是类(class)的主人......我感觉到你了!
有关此行为原因的更多详细信息read this ?
关于java - 使用 Java 反射更改私有(private)静态最终字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3301635/