假设 no 阻止您执行此操作,您可以使用 绕过并重置修饰符以摆脱 ,并实际修改字段。SecurityManager
setAccessible
private
final
private static final
下面是一个示例:
import java.lang.reflect.*;
public class EverythingIsTrue {
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
public static void main(String args[]) throws Exception {
setFinalStatic(Boolean.class.getField("FALSE"), true);
System.out.format("Everything is %s", false); // "Everything is true"
}
}
假设不被抛出,上面的代码将打印 。SecurityException
"Everything is true"
这里实际执行的操作如下:
- 基元值和 in 会自动装箱以引用类型“常量”和
boolean
true
false
main
Boolean
Boolean.TRUE
Boolean.FALSE
- 反射用于更改
公共静态最终布尔值。FALSE
引用Boolean
Boolean.TRUE
- 因此,随后每当 a 被自动装箱到 时,它所指的与 所指的相同
false
Boolean.FALSE
Boolean
Boolean.TRUE
- 现在的一切都是
"false"
"true"
相关问题
警告
每当您做这样的事情时,都应该格外小心。它可能不起作用,因为 可能存在 ,但即使它不存在,根据使用模式,它可能有效,也可能不起作用。SecurityManager
JLS 17.5.3 最终字段的后续修改
在某些情况下,例如反序列化,系统需要在构造后更改对象的字段。 字段可以通过反射和其他依赖于实现的方式进行更改。这具有合理语义的唯一模式是构造对象,然后更新对象的字段。在完成对对象字段的所有更新之前,不应使该对象对其他线程可见,也不应读取该字段。字段冻结既发生在设置字段的构造函数的末尾,也发生在每次通过反射或其他特殊机制修改字段之后。final
final
final
final
final
final
final
final
即便如此,也存在许多并发症。如果在字段声明中将字段初始化为编译时常量,则可能不会观察到对该字段的更改,因为在编译时将使用编译时常量替换该字段的使用。final
final
final
另一个问题是规范允许对字段进行主动优化。在线程中,允许使用构造函数中未发生的最终字段的修改对字段的读取进行重新排序。final
final
另请参见
-
JLS 15.28 常量表达式
- 这种技术不太可能与基元一起使用,因为它是可作为编译时常量内联的,因此“new”值可能无法观察
private static final boolean
附录:关于按位操作
本质上
field.getModifiers() & ~Modifier.FINAL
关闭 与 相对应的位。 是按位和,并且是按位补码。Modifier.FINAL
field.getModifiers()
&
~
另请参见
记住常量表达式
仍然无法解决这个问题?,像我一样陷入抑郁症?您的代码看起来像这样吗?
public class A {
private final String myVar = "Some Value";
}
阅读有关此答案的评论,特别是@Pshemo的评论,它提醒我常量表达式的处理方式不同,因此无法对其进行修改。因此,您需要将代码更改为如下所示:
public class A {
private final String myVar;
private A() {
myVar = "Some Value";
}
}
如果您不是该课程的所有者...我感觉到你了!
有关此行为原因的更多详细信息,请阅读此内容?