问题
java code as follow.
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Test {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
C c = new C();
Field field = c.getClass().getDeclaredField("NAME");
field.setAccessible(true);
System.out.println(field.get(c));//Cause program exception on line 15 while using method get(java.lang.reflect.Field#get).
Field modifiers = field.getClass().getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
System.out.println(Modifier.toString(field.getModifiers()));
field.set(c,"James");
System.out.println(field.get(c));
}
}
class C{
private static final String NAME = "Clive";
public String toString(){
return NAME;
}
}
An exception occured when I use java.lang.reflect.Field#set。Exception information as follow. However,if I delete the code(System.out.println(field.get(c));) on line 9,no exception occured
Exception in thread "main" java.lang.IllegalAccessException: Can not set static final java.lang.String field C.NAME to java.lang.String
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:73)
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:77)
at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.java:77)
at java.lang.reflect.Field.set(Field.java:741)
at Test.main(Test.java:15)
回答1:
Field
lazily creates an object called a FieldAccessor
which is actually responsible for get
and set
operations. This can be seen in the source code for Field.get (archive). You can click on the method getFieldAccessor
to go deeper in to the call stack. This will (at the moment) eventually take you to a method sun.reflect.UnsafeFieldAccessorFactory.newFieldAccessor (archive) where you can see that the modifiers are read once and then baked in to the actual type of the field accessor.
Calling Field.get
before changing the modifiers affects the output because it causes the field accessor to be instantiated before final
is removed.
You could possibly use something like the following bit of code to clear the field accessors:
public static void clearFieldAccessors(Field field)
throws ReflectiveOperationException {
Field fa = Field.class.getDeclaredField("fieldAccessor");
fa.setAccessible(true);
fa.set(field, null);
Field ofa = Field.class.getDeclaredField("overrideFieldAccessor");
ofa.setAccessible(true);
ofa.set(field, null);
Field rf = Field.class.getDeclaredField("root");
rf.setAccessible(true);
Field root = (Field) rf.get(field);
if (root != null) {
clearFieldAccessors(root);
}
}
Using that causes the code in the question to pass, if you insert clearFieldAccessors(field)
in between field.get(...)
and field.set(...)
.
There is, of course, no guarantee that any of this has to work, and it's possible that the code in clearFieldAccessors
will cause some problem that I'm unaware of.
回答2:
You are facing this issue because of the following line
System.out.println(field.get(c));
Since Field by default assumes the modifier as final, JDK will cache the field the moment you invoke any operations on it. Now, in your later part of the code you are modifying the access of the field through the following line
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
But since you have not invalidated the Cache, you are getting the following exception.
Hence, if you comment out the get statement, your access modifier logic will work without throwing any exceptions
In a nutshell, you need to invoke your modifier logic before invoking any of the operations associated with field. The first time you invoke it, the metadata would be cached
来源:https://stackoverflow.com/questions/49102935/why-i-cant-use-method-getjava-lang-reflect-fieldget-before-changing-fields