简体   繁体   English

更改“静态最终”字段的值

[英]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 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." “如果在字段声明中将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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM