使用Java反射更改私有静态最终字段
我有一个private static final
字段的类,不幸的是,我需要在运行时更改。
使用反射我得到这个错误: java.lang.IllegalAccessException: Can not set static final boolean field
有什么方法可以改变价值吗?
Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);
假设没有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"
。
这里实际做了什么如下:
main
中的原始boolean
值true
和false
被自动绑定到引用类型Boolean
“常量” Boolean.TRUE
和Boolean.FALSE
public static final Boolean.FALSE
来引用由Boolean.TRUE
引用的Boolean
false
被autoboxed到Boolean.FALSE
,它指的是相同的Boolean
作为一个由refered到Boolean.TRUE
"false"
东西都是"true"
相关问题
static final File.separatorChar
以进行单元测试 Integer
的缓存混淆的例子,突变String
等 注意事项
当你做这样的事情时应该非常小心。 它可能无法工作,因为SecurityManager
可能存在,但即使它不存在,根据使用模式,它可能也可能不起作用。
JLS 17.5.3最终字段的后续修改
在某些情况下,如反序列化,系统需要在构建后更改对象的final
字段。 final
字段可以通过反射和其他实现相关手段进行更改。 其中有合理的语义的唯一模式是在其中构建对象,然后更新对象的final
字段。 对象不应该对其他线程可见,也不应该读取final
字段,直到完成对对象final
字段的所有更新。 final
字段的冻结发生在final
字段被设置的构造函数的末尾,并且在通过反射或其他特殊机制每次修改final
字段之后立即出现。
即使那样,也有一些并发症。 如果final
字段在字段声明中初始化为编译时常量,则可能无法观察对final
字段的更改,因为在编译时将该final
字段的使用替换为编译时常量。
另一个问题是规范允许对final
字段进行积极的优化。 在一个线程内,允许对final
字段的读取进行重新排序,这些修改不会在构造函数中发生。
也可以看看
private static final boolean
,因为它可以作为编译时常量进行内联,因此“新”值可能不可观察 附录:关于按位操作
从本质上讲,
field.getModifiers() & ~Modifier.FINAL
从field.getModifiers()
关闭对应于Modifier.FINAL
的位。 &
是按位和,而~
是按位补码。
也可以看看
如果分配给static final boolean
字段的值在编译时已知,则它是一个常量。 基元或String
类型的字段可以是编译时常量。 任何引用该字段的代码中都会内联一个常量。 由于该字段实际上并未在运行时读取,因此更改它将不起作用。
Java语言规范说明了这一点:
如果一个字段是一个常量变量(§4.12.4),那么删除关键字final或改变它的值不会破坏与预先存在的二进制文件的兼容性,通过使它们不运行,但它们不会看到任何新的用法值除非它们被重新编译。 即使使用本身不是编译时常量表达式(第15.28节),情况也是如此,
这是一个例子:
class Flag {
static final boolean FLAG = true;
}
class Checker {
public static void main(String... argv) {
System.out.println(Flag.FLAG);
}
}
如果你反编译Checker
,你会看到,而不是引用Flag.FLAG
,代码只是将1( true
)值推入堆栈(指令#3)。
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: iconst_1
4: invokevirtual #3; //Method java/io/PrintStream.println:(Z)V
7: return
来自Java语言规范第17章第17.5.4节“写保护字段”的一点好奇心:
通常,最终和静态的字段可能不会被修改。 但是,System.in,System.out和System.err是静态最终字段,由于传统原因,它们必须允许通过System.setIn,System.setOut和System.setErr方法进行更改。 我们将这些字段称为被写保护以区别于普通的最终字段。
资料来源:http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4
链接地址: http://www.djcxy.com/p/21209.html上一篇: Change private static final field using Java reflection
下一篇: Reflecting parameter name: abuse of C# lambda expressions or Syntax brilliance?