我们说我有一个
class Foo(){
public final static int bar = -1;
}
反汇编的字节码看起来像这样
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
是的,这令我感到惊讶。
我希望有一个<clinit>
方法,其中包含bar
的赋值,然后我可以替换它。 (当我删除final
修饰符时会发生这种情况。)
如何更改final
字段的值?我该怎么做?
答案 0 :(得分:5)
您的期望不正确。用整数文字初始化的static final int
将是编译时常量。编译时常量由字节码编译器内联。
无法在运行时更改值或使用字节码修改。字节码编译器所做的内联无法解开。 重新编译类及其依赖类是更改编译时常量值的唯一易处理方法。
请注意,这不仅仅是Java编译器实现的一个不方便的工件。编译时常量的这种处理是由JLS强制执行的。例如,JLS 17.5.3说明了尝试使用反射更改编译时常量final
:
“如果在字段声明中将
final
字段初始化为常量表达式(第15.28节),则可能无法观察到final
字段的更改,因为使用了final
在编译时将字段替换为常量表达式的值。“
换句话说,更改Foo.bar
的反射API调用可能会成功,但内联的实际值不会更改。实际上,唯一可能看到更新值的代码是使用反射读取Foo.bar
的代码!
一种可能的解决方法是以使其不是编译时常量的方式声明常量。例如:
class Foo() {
public final static int bar = Integer.parseInt("-1");
}