在前一次reflection之后,设置静态最终字段的Javareflection失败
在Java中,事实certificate,字段访问器被缓存,并且使用访问器具有副作用。 例如:
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
而
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)
这两个reflection访问当然发生在我的代码库的非常不同的部分,我真的不想改变第一个来修复第二个。 有没有办法改变第二个reflection访问以确保它在两种情况下都成功?
我试着看看Field
对象,它没有任何看似他们会帮助的方法。 在调试器中,我注意到在第一个示例中返回的第二个Field
上设置了overrideFieldAccessor
,但没有看到修改器的更改。 不过,我不知道该怎么办。
如果它openjdk-8
,我正在使用openjdk-8
。
如果你想要修改器hack(不要忘记它是完全黑客 ),你需要在第一次访问该字段之前更改modifiers
私有字段。
所以,在你做f.getInt(null);
,你需要这样做:
mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
原因是,无论您拥有多少不同的实际java.lang.reflect.Field
对象,都只为类(*)的每个字段创建一个内部FieldAccessor
对象。 当在UnsafeFieldAccessorFactory中构造FieldAccessor实现时,检查final
修饰符一次。
当确定你无法访问final static
字段时(因为setAccessible
覆盖不起作用,但非静态的最终字段,但不适用于static
最终字段),它会在每次后续reflection时都失败,即使是不同的Field
对象,因为它一直使用相同的FieldAccessor
。
(*)禁止同步问题; 作为评论中Field
提及的源代码:
//注意这里没有使用同步。 为给定的字段生成多个FieldAccessor是正确的(尽管效率不高)。