首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么在更改字段的修饰符之前不能使用get(java.lang.reflect.Field#get)方法

为什么在更改字段的修饰符之前不能使用get(java.lang.reflect.Field#get)方法
EN

Stack Overflow用户
提问于 2018-03-05 11:07:09
回答 2查看 849关注 0票数 3

java代码如下所示。

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

当我使用java.lang.reflect.Field#set时,发生异常。异常信息如下。但是,如果删除第9行的代码(System.out.println(field.get(C));),则不会出现异常

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)
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-03-05 12:46:31

Field懒惰地创建了一个名为FieldAccessor的对象,它实际上负责getset操作。这可以在Field.get (archive)的源代码中看到。您可以单击方法getFieldAccessor更深入地了解调用堆栈。这将(目前)最终将您带到一个方法sun.reflect.UnsafeFieldAccessorFactory.newFieldAccessor (archive),您可以在其中看到修饰符被读取一次,然后被烘焙到字段访问器的实际类型中。

在更改修饰符之前调用Field.get会影响输出,因为这会导致在删除final之前实例化字段访问器。

您可以使用类似以下代码的代码来清除字段访问器:

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

如果在field.get(...)field.set(...)之间插入clearFieldAccessors(field),则使用该选项会导致问题中的代码通过。

当然,不能保证所有这些都能正常工作,而且clearFieldAccessors中的代码可能会导致一些我不知道的问题。

票数 3
EN

Stack Overflow用户

发布于 2018-03-05 12:07:11

您正面临这个问题,因为下面这行代码

System.out.println(field.get(c));

由于Field默认假设修饰符为final,因此JDK将在您对其调用任何操作时缓存该字段。现在,在后面的代码部分中,您将通过以下代码行修改字段的访问

 modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);

但是,由于您尚未使Cache无效,因此您将得到以下异常。

因此,如果注释掉get语句,则访问修饰符逻辑将正常工作,不会抛出任何异常

简而言之,在调用与field关联的任何操作之前,您需要调用您的修饰符逻辑。第一次调用它时,元数据将被缓存

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49102935

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档