[英]Changing the value of a “static final” field
Let's say I have a 假设我有一个
class Foo(){
public final static int bar = -1;
}
disassembled bytecode will look something like this 分解后的字节码看起来像这样
super public class Foo
version 51:0
{
public static final Field bar:I = int -1;
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
} // end Class Foo
And yes, this surprised me. 是的,这让我感到惊讶。 I would have expected there to be a <clinit>
method containing an assignment to bar
that I could then replace. 我本来希望有一个<clinit>
方法,其中包含一个bar
赋值,然后我可以替换该赋值。 (Which is indeed what happens when I remove the final
modifier.) (实际上,当我删除final
修饰符时会发生什么。)
How do I change the value for a final
field? 如何更改final
字段的值? What do I hook into? 我应该迷上什么?
Your expectation is incorrect. 您的期望是不正确的。 A static final int
which is initialized with an integer literal will be a compile-time constant. 用整数文字量初始化的static final int
将是编译时常量。 Compile-time constants get inlined by the bytecode compiler. 字节码编译器内联了编译时常量。
It will not be possible to change the value at runtime, or using bytecode modification. 在运行时或使用字节码修改将无法更改该值。 The inlining that the bytecode compiler did can't be unwound. 字节码编译器所做的内联无法解开。 Recompilation of the class and its dependent classes is the only tractable way to change the value of a compile-time constant. 重新编译该类及其从属类是更改编译时常量值的唯一可行方法。
Note this is not just an inconvenient artifact of the Java compiler implementation. 注意,这不仅仅是Java编译器实现的不便之处。 This handling of compile-time constants is mandated by the JLS. JLS要求对编译时常量进行这种处理。 For example, JLS 17.5.3 says this about trying to change a compile-time constant final
using reflection: 例如,JLS 17.5.3表示了有关尝试使用反射更改编译时常量final
:
"If a
final
field is initialized to a constant expression (§15.28) in the field declaration, changes to thefinal
field may not be observed, since uses of thatfinal
field are replaced at compile time with the value of the constant expression." “如果在字段声明中将final
字段初始化为常量表达式(第15.28节),则可能不会观察到对final
字段的更改,因为在编译时会用常量表达式的值替换该final
字段的使用。”
In other words, a reflective API call to change Foo.bar
may appear to succeed, but the actual value that has been inlined doesn't change. 换句话说,更改Foo.bar
的反射API调用似乎成功了,但是内联的实际值没有改变。 In fact, the only code that is likely to see the updated value is code that reads Foo.bar
using reflection! 实际上,唯一可能看到更新值的代码是使用反射读取Foo.bar
代码!
One possible workaround would be to declare the constant in a way that makes it not a compile-time constant. 一种可能的解决方法是,以使其不成为编译时常量的方式声明该常量。 For example: 例如:
class Foo() {
public final static int bar = Integer.parseInt("-1");
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.