Java – Why can’t I use the method get before changing the modifier of the field (java.lang.reflect.Field#get)

Why can’t I use the method get before changing the modifier of the field (java.lang.reflect.Field#get)… here is a solution to the problem.

Why can’t I use the method get before changing the modifier of the field (java.lang.reflect.Field#get)

The Java code is as follows

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 occurred when using java.lang.reflect.Field#set. The exception information is as follows. However, if I delete the code in line 9 (System.out.println(field.get(c);), no exception occurs

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)

Solution

Field

deferred creates an object called FieldAccessor, which is actually responsible for get and set operations. This can be done Field.get is seen in the source code (archive)。 You can click the getFieldAccessor method to gain a deeper understanding of the call stack. This will (at the moment) finally take you to a method sun.reflect.UnsafeFieldAccessorFactory.newFieldAccessor ( archive), where you can see the modifier being read once and then baked into the actual type of the field accessor.

Calling Field.get before changing the modifier affects the output because it causes the field accessor to be instantiated before the final is removed.

You can clear the field accessor with something similar to the following code:

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);
    }
}

If you insert clearFieldAccessors(field) between field.get(...) and field, using it will cause the code in question to pass through .set(...).

Of course, there is no guarantee that any of them will work, and the code in clearFieldAccessors may cause some problems that I am not aware of.

Related Problems and Solutions