在 Java 中,事实证明字段访问器会被缓存,并且使用访问器会产生副作用。例如:
class A {
private static final int FOO = 5;
}
Field f = A.class.getDeclaredField("FOO");
f.setAccessible(true);
f.getInt(null); // succeeds
Field mf = Field.class.getDeclaredField("modifiers" );
mf.setAccessible(true);
f = A.class.getDeclaredField("FOO");
f.setAccessible(true);
mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
f.setInt(null, 6); // fails
whereas
class A {
private static final int FOO = 5;
}
Field mf = Field.class.getDeclaredField("modifiers" );
mf.setAccessible(true);
f = A.class.getDeclaredField("FOO");
f.setAccessible(true);
mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
f.setInt(null, 6); // succeeds
这是失败的堆栈跟踪的相关位:
java.lang.IllegalAccessException: Can not set static final int field A.FOO to (int)6
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:100)
at sun.reflect.UnsafeQualifiedStaticIntegerFieldAccessorImpl.setInt(UnsafeQualifiedStaticIntegerFieldAccessorImpl.java:129)
at java.lang.reflect.Field.setInt(Field.java:949)
这两个反射访问当然发生在我的代码库的非常不同的部分,并且我真的不想更改第一个来修复第二个。有没有办法更改第二个反射访问以确保它在两种情况下都成功?
我试着看看Field
对象,并且它没有任何看起来有帮助的方法。在调试器中,我注意到overrideFieldAccessor
设置在第二个Field
在第一个示例中返回,并且看不到修饰符的更改。但我不知道该怎么办。
如果有什么不同,我正在使用openjdk-8
.
如果你想要修改器 hack(不要忘记这完全是一个黑客行为 https://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection)要工作,您需要更改modifiers
私人领域before第一次访问该字段时。
所以,在你这样做之前f.getInt(null);
,你需要做:
mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
原因是只有一个内部FieldAccessor
为类 (*) 的每个字段创建对象,无论实际有多少个不同的字段java.lang.reflect.Field
你拥有的对象。以及支票final
修饰符在构造 FieldAccessor 实现时完成一次UnsafeFieldAccessorFactory http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/sun/reflect/UnsafeFieldAccessorFactory.java#UnsafeFieldAccessorFactory.newFieldAccessor%28java.lang.reflect.Field%2Cboolean%29.
当确定您无法访问时final static
字段(因为,setAccessible
覆盖不起作用,但非静态最终字段不起作用,但不适用于static
最终字段),即使通过不同的方式,它对于每个后续反射都会失败Field
对象,因为它一直使用相同的FieldAccessor
.
(*) 排除同步问题;作为源代码Field
评论中提到:
// 注意这里没有使用同步。它是正确的
(虽然效率不高)为一个字段生成多个 FieldAccessor
给定字段。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)