改变"静态决赛的价值"领域

时间:2018-02-13 13:40:13

标签: java bytecode instrumentation java-bytecode-asm soot

我们说我有一个

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字段的值?我该怎么做?

1 个答案:

答案 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");
}