使用 Java 反射更改私有静态最终字段 [英] Change private static final field using Java reflection
问题描述
我有一个带有 private static final
字段的类,不幸的是,我需要在运行时更改它.
I have a class with a private static final
field that, unfortunately, I need to change it at run-time.
使用反射我得到这个错误:java.lang.IllegalAccessException: Can not set static final boolean field
Using reflection I get this error: java.lang.IllegalAccessException: Can not set static final boolean field
有什么办法可以改变这个值吗?
Is there any way to change the value?
Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);
推荐答案
假设没有 SecurityManager
阻止您这样做,您可以使用 setAccessible
绕过 private
并重置修饰符去掉final
,实际上修改了一个private static final
字段.
Assuming no SecurityManager
is preventing you from doing this, you can use setAccessible
to get around private
and resetting the modifier to get rid of final
, and actually modify a private static final
field.
这是一个例子:
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"
.
Assuming no SecurityException
is thrown, the above code prints "Everything is true"
.
这里实际做的事情如下:
What's actually done here is as follows:
main
中的原始boolean
值true
和false
被自动装箱为引用类型Boolean
常数"Boolean.TRUE
和Boolean.FALSE
- 反射用于更改
public static final Boolean.FALSE
引用Boolean.TRUE
引用的 - 因此,随后每当
false
自动装箱为Boolean.FALSE
时,它引用的Boolean
与所引用的相同通过Boolean.TRUE
- 所有
假"
现在都是真"
Boolean
- The primitive
boolean
valuestrue
andfalse
inmain
are autoboxed to reference typeBoolean
"constants"Boolean.TRUE
andBoolean.FALSE
- Reflection is used to change the
public static final Boolean.FALSE
to refer to theBoolean
referred to byBoolean.TRUE
- As a result, subsequently whenever a
false
is autoboxed toBoolean.FALSE
, it refers to the sameBoolean
as the one refered to byBoolean.TRUE
- Everything that was
"false"
now is"true"
- 使用反射更改
static final File.separatorChar
用于单元测试 - 如何将 setAccessible 限制为仅合法"使用?一>
- 有搞乱
Integer
的缓存、改变String
等的例子
- Using reflection to change
static final File.separatorChar
for unit testing - How to limit setAccessible to only "legitimate" uses?
- Has examples of messing with
Integer
's cache, mutating aString
, etc
每当您执行此类操作时都应格外小心.它可能不起作用,因为
SecurityManager
可能存在,但即使没有,根据使用模式,它可能会也可能不会起作用.Extreme care should be taken whenever you do something like this. It may not work because a
SecurityManager
may be present, but even if it doesn't, depending on usage pattern, it may or may not work.在某些情况下,例如反序列化,系统将需要在构造后更改对象的
final
字段.final
字段可以通过反射和其他依赖于实现的方式来改变.唯一具有合理语义的模式是构造一个对象,然后更新对象的final
字段.对象不应该对其他线程可见,也不应该读取final
字段,直到对象的final
字段的所有更新完成.final
字段的冻结发生在设置了final
字段的构造函数的末尾,以及在每次修改final
之后立即发生场通过反射或其他特殊机制.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.即便如此,仍有许多并发症.如果在字段声明中将
final
字段初始化为编译时常量,则可能不会观察到对final
字段的更改,因为使用了该final
字段在编译时替换为编译时常量.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.另一个问题是规范允许对
final
字段进行积极优化.在一个线程中,允许对final
字段的读取重新排序,并在构造函数中没有发生对 final 字段的修改.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 常量表达式一>
- 这种技术不太可能与原始的
private static final boolean
一起使用,因为它可以作为编译时常量内联,因此new"价值可能无法观察 - JLS 15.28 Constant Expression
- It's unlikely that this technique works with a primitive
private static final boolean
, because it's inlineable as a compile-time constant and thus the "new" value may not be observable
本质上,
field.getModifiers() & ~Modifier.FINAL
关闭与
field.getModifiers()
中的Modifier.FINAL
对应的位.&
是按位与,而~
是按位补码.turns off the bit corresponding to
Modifier.FINAL
fromfield.getModifiers()
.&
is the bitwise-and, and~
is the bitwise-complement.还是解决不了?,像我一样陷入抑郁了吗?你的代码是这样的吗?
Still not being able to solve this?, have fallen onto depression like I did for it? Does your code looks like this?
public class A { private final String myVar = "Some Value"; }
阅读对此答案的评论,特别是@Pshemo 的评论,它提醒我常量表达式 的处理方式不同,因此不可能修改它.因此,您需要将代码更改为如下所示:
Reading the comments on this answer, specially the one by @Pshemo, it reminded me that Constant Expressions are handled different so it will be impossible to modify it. Hence you will need to change your code to look like this:
public class A { private final String myVar; private A() { myVar = "Some Value"; } }
如果你不是班级的主人......我觉得你是!
有关此行为原因的更多详细信息阅读此内容?
For more details about why this behavior read this?
这篇关于使用 Java 反射更改私有静态最终字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- It's unlikely that this technique works with a primitive
See also
- 这种技术不太可能与原始的
- Has examples of messing with
- 有搞乱