1

假设我有一个

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

4

1 回答 1

5

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

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

请注意,这不仅仅是 Java 编译器实现的一个不方便的工件。这种编译时常量的处理是由 JLS 规定的。例如,JLS 17.5.3 说明了尝试final使用反射更改编译时常量:

“如果final在字段声明中将字段初始化为常量表达式(第 15.28 节),则final可能不会观察到字段的更改,因为该final字段的使用在编译时被常量表达式的值替换。”

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

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

class Foo() {
    public final static int bar = Integer.parseInt("-1");
}
于 2018-02-13T13:56:04.567 回答