why I can`t use method get(java.lang.reflect.Field#get) before changing field`s modifiers

让人想犯罪 __ 提交于 2020-01-21 12:07:09

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!