更改 "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");
}
假设我有一个
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 thefinal
field may not be observed, since uses of thatfinal
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");
}