更改 "static final" 字段的值

Changing the value of a "static final" field

假设我有一个

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 字段的值?我要连接什么?

您的期望不正确。用整数文字初始化的 static final int 将是编译时常量。编译时常量由字节码编译器内联。

无法在运行时更改值,也无法使用字节码修改。字节码编译器所做的内联无法展开。 重新编译 class 及其依赖的 classes 是更改编译时常量值的唯一易于处理的方法。

请注意,这不仅仅是 Java 编译器实现的一个不方便的产物。这种编译时常量的处理由 JLS 强制执行。例如,JLS 17.5.3 说这是关于尝试使用反射更改编译时常量 final

"If a final field is initialized to a constant expression (§15.28) in the field declaration, changes to the final field may not be observed, since uses of that final field are replaced at compile time with the value of the constant expression."

换句话说,反射 API 更改 Foo.bar 的调用可能看起来成功了,但内联的实际值并没有改变。事实上,唯一可能看到更新值的代码是使用反射读取 Foo.bar 的代码!

一种可能的解决方法是以一种使其不是编译时常量的方式声明常量。例如:

class Foo() {
    public final static int bar = Integer.parseInt("-1");
}