我试图private static final使用反射来更改字段的值(是的,我知道这可能是一个非常糟糕的主意)。\n而且,在大多数情况下,使用以下代码它可以正常工作:
import java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\n\npublic class A {\n\n    public static void main(String[] args) throws ReflectiveOperationException {\n        System.out.println("Before :: " + B.get());\n        Field field = B.class.getDeclaredField("arr");\n        field.setAccessible(true);\n        // System.out.println("Peek   :: " + ((String[]) field.get(null))[0]);\n        Field modifiersField = Field.class.getDeclaredField("modifiers");\n        modifiersField.setAccessible(true);\n        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);\n        field.set(null, new String[] { "Good bye, World!" });\n        System.out.println("After  :: " + B.get());\n    }\n}\n\nclass B {\n    private static final String[] arr = new String[] { "Hello, World!" };\n\n    public static String get() {\n        return arr[0];\n    }\n}\n按预期打印:
\n\nBefore :: Hello, World!\nAfter  :: Good bye, World!\nget当我在设置字段值之前尝试通过反射set获取字段值时,就会出现问题。\n也就是说,如果我取消注释上面示例中的注释行,我会得到以下结果:
Before :: Hello, World!\nPeek   :: Hello, World!\nException in thread "main" java.lang.IllegalAccessException: Can not set static final [Ljava.lang.String; field B.arr to [Ljava.lang.String;\n        at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)\n        at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)\n        at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.java:77)\n        at java.lang.reflect.Field.set(Field.java:764)\n        at A.main(A.java:14)\n为什么会发生这种情况?\n我尝试accessible在调用后再次设置标志get,但它没有帮助。\n我尝试了许多其他似乎也没有帮助的事情...
感谢您的帮助!
\n\n编辑:使用反射更改静态最终 File.separatorChar 进行单元测试中有一个答案元素?(请参阅@Rog\xc3\xa9rio 的“重要更新”)。
\n\n\n\n\n重要更新:上述解决方案并不适用于所有情况。如果该字段在重置之前可以通过反射进行访问和读取,则会
\n\nIllegalAccessException抛出异常。它失败是因为 Reflection API 创建了FieldAccessor缓存和重用的内部对象(请参阅 java.lang.reflect.Field#acquireFieldAccessor(boolean) 实现)。\n 失败的示例测试代码:Run Code Online (Sandbox Code Playgroud)\nField f = File.class.getField("separatorChar"); f.setAccessible(true); f.get(null);\n// call setFinalStatic as before: throws IllegalAccessException\n
遗憾的是,它没有说明如何解决这个问题......我如何“重置”该字段?
\n我不知道在回答这个问题时应该深入细节。但这里是所发生情况的简短摘要:
当您进行反射Field#get调用时,调用将在内部(经过几次安全检查后)委托给sun.reflect.FieldAccessor. 这是一个内部接口,顾名思义,提供对字段值的访问。内部使用的实例FieldAccessor是惰性创建的,“缓存”以供以后使用,甚至在多个Field实例之间共享。
该接口有许多不同的实现FieldAccessor。这些实现专门用于普通字段、静态字段或可通过调用访问的私有字段的各种情况setAccessible(true)。例如,在您的情况下,有一个UnsafeQualifiedStaticObjectFieldAccessorImpl涉及,并且名称已经表明这只是几十个专业之一。
其中许多FieldAccessor实现存储内部状态,它描述字段的一些属性。例如,该字段是“只读”还是final(!)。
要点是:FieldAccessor执行反射Field#get调用时创建的 与稍后将用于反射调用的相同Field#set。但当创建时FieldAccessor,该字段仍被视为final。仅在创建FieldAccessor.
因此,最简单的解决方案是确保在执行第一次反射调用之前final更改该字段的状态。这样,内部创建的 就是在“非最终”字段上运行的:FieldAccessor
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class A {
    public static void main(String[] args) throws Exception {
        System.out.println("Before :: " + B.get());
        Field field = B.class.getDeclaredField("arr");
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        System.out.println("Peek   :: " + ((String[]) field.get(null))[0]);
        field.set(null, new String[] { "Good bye, World!" });
        System.out.println("After  :: " + B.get());
    }
}
class B {
    private static final String[] arr = new String[] { "Hello, World!" };
    public static String get() {
        return arr[0];
    }
}
我不应该提及这一点。人们会这样做。但无论如何:
从技术上讲,还可以brutallyHackYourWayThroughInternalClasses修改FieldAccessor内部创建的 ,以便随后允许修改该字段,即使它最初是为该final字段的版本创建的:
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class A {
    public static void main(String[] args) throws Exception {
        System.out.println("Before :: " + B.get());
        Field field = B.class.getDeclaredField("arr");
        field.setAccessible(true);
        System.out.println("Peek   :: " + ((String[]) field.get(null))[0]);
        brutallyHackYourWayThroughInternalClasses(field);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, new String[] { "Good bye, World!" });
        System.out.println("After  :: " + B.get());
    }
    private static void brutallyHackYourWayThroughInternalClasses(Field field)
        throws Exception
    {
        Field overrideFieldAccessorField = 
            Field.class.getDeclaredField("overrideFieldAccessor");
        overrideFieldAccessorField.setAccessible(true);
        Object overrideFieldAccessorValue = 
            overrideFieldAccessorField.get(field);
        Class<?> unsafeFieldAccessorImplClass = 
            Class.forName("sun.reflect.UnsafeFieldAccessorImpl");
        Field isFinalField = 
            unsafeFieldAccessorImplClass.getDeclaredField("isFinal");
        isFinalField.setAccessible(true);
        isFinalField.set(overrideFieldAccessorValue, false);
        Class<?> unsafeQualifiedStaticFieldAccessorImplClass = 
            Class.forName("sun.reflect.UnsafeQualifiedStaticFieldAccessorImpl");
        Field isReadOnlyField = 
            unsafeQualifiedStaticFieldAccessorImplClass.getDeclaredField(
                "isReadOnly");
        isReadOnlyField.setAccessible(true);
        isReadOnlyField.set(overrideFieldAccessorValue, false);
    }
}
class B {
    private static final String[] arr = new String[] { "Hello, World!" };
    public static String get() {
        return arr[0];
    }
}
| 归档时间: | 
 | 
| 查看次数: | 2921 次 | 
| 最近记录: |