Java 设置静态最终字段的反射在上一次反射后失败
Java reflection to set static final field fails after previous reflection
在 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
而
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(不要忘记 it is a total hack)起作用,你需要在第一个 之前更改 modifiers
私有字段您访问该字段的时间。
所以,在你f.getInt(null);
之前,你需要做:
mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
原因是,无论您有多少个不同的实际 java.lang.reflect.Field
对象,都只会为 class (*) 的每个字段创建一个内部 FieldAccessor
对象。并且在 UnsafeFieldAccessorFactory
.
中构造 FieldAccessor 实现时,对 final
修饰符的检查完成一次
当确定您无法访问 final static
字段时(因为 setAccessible
覆盖不起作用,但非静态最终字段,但不适用于 static
最终字段),即使通过不同的 Field
对象,它也会在每次后续反射中不断失败,因为它一直使用相同的 FieldAccessor
.
(*) 排除同步问题;作为 Field
的源代码,在评论中提到:
// NOTE that there is no synchronization used here. It is correct
(though not efficient) to generate more than one FieldAccessor for a
given Field.
在 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
而
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(不要忘记 it is a total hack)起作用,你需要在第一个 之前更改 modifiers
私有字段您访问该字段的时间。
所以,在你f.getInt(null);
之前,你需要做:
mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
原因是,无论您有多少个不同的实际 java.lang.reflect.Field
对象,都只会为 class (*) 的每个字段创建一个内部 FieldAccessor
对象。并且在 UnsafeFieldAccessorFactory
.
final
修饰符的检查完成一次
当确定您无法访问 final static
字段时(因为 setAccessible
覆盖不起作用,但非静态最终字段,但不适用于 static
最终字段),即使通过不同的 Field
对象,它也会在每次后续反射中不断失败,因为它一直使用相同的 FieldAccessor
.
(*) 排除同步问题;作为 Field
的源代码,在评论中提到:
// NOTE that there is no synchronization used here. It is correct (though not efficient) to generate more than one FieldAccessor for a given Field.