Java反射 - setAccessible的影响(true)

dnc*_*253 98 java reflection

我正在使用一些注释来动态设置类中的字段值.因为无论是public,protected还是private,我想这样做,setAccessible(true)每次调用set()方法之前,我都调用Field对象.我的问题是这个setAccessible()电话会对电影本身产生什么样的影响?

更具体地说,它是一个私有字段和这组代码调用setAccessible(true).如果代码中的其他位置然后通过反射检索相同的字段,那么该字段是否已经可访问?或者getDeclaredFields(),getDeclaredField()方法每次都返回Field对象的新实例吗?

我想另一种陈述问题的方法是,如果我打电话setAccessible(true),在我完成之后将它设置回原始值有多重要?

Mor*_*sen 79

随着setAccessible()你改变的行为AccessibleObject,即Field实例,而不是类的实际领域.这是文档(摘录):

true表示反射对象在使用时应禁止检查Java语言访问控制

一个可运行的例子:

public class FieldAccessible {
    public static class MyClass {
        private String theField;
    }

    public static void main(String[] args) throws Exception {
        MyClass myClass = new MyClass();
        Field field1 = myClass.getClass().getDeclaredField("theField");
        field1.setAccessible(true);
        System.out.println(field1.get(myClass)); // no exception
        Field field2 = myClass.getClass().getDeclaredField("theField");
        System.out.println(field2.get(myClass)); // IllegalAccessException
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 是的,这只是示例代码。我的意思是,“抛出异常”也处理“NoSuchFieldException”,但您可能希望以更复杂的方式处理它。 (2认同)

Jör*_*ann 30

getDeclaredField方法每次都必须返回一个新对象,因为该对象具有可变accessible标志.所以没有必要重置标志.您可以在此博客文章中找到完整的详细信息.


Col*_*l-E 8

正如其他发帖者所指出的,setAccessible仅适用于您的该实例java.lang.reflect.Field,因此不需要将可访问性设置回其原始状态。

然而...

如果您希望调用field.setAccessible(true)持久,您需要使用java.lang.Class和中的底层方法java.lang.reflect.Field。面向公众的方法会向您发送实例的副本Field,因此每次您执行类似操作后它都会“忘记”class.getField(name)

import java.lang.reflect.*;
import sun.reflect.FieldAccessor;

public class Reflect {
    private static Method privateGetDeclaredFields;
    private static Method getFieldAccessor;

    public static Field[] fields(Class<?> clazz) throws Exception {
        return (Field[]) privateGetDeclaredFields.invoke(clazz, false);
    }

    public static <T> T get(Object instance, Field field) throws Exception {
        return ((FieldAccessor) getFieldAccessor.invoke(field, instance)).get(instance);
    }

    public static void set(Object instance, Field field, Object value) throws Exception {
        ((FieldAccessor) getFieldAccessor.invoke(field, instance)).set(instance, value);
    }

    static {
        try {
            // These are used to access the direct Field instances instead of the copies you normally get through #getDeclaredFields.
            privateGetDeclaredFields = Class.class.getDeclaredMethod("privateGetDeclaredFields", boolean.class);
            privateGetDeclaredFields.setAccessible(true);
            getFieldAccessor = Field.class.getDeclaredMethod("getFieldAccessor", Object.class);
            getFieldAccessor.setAccessible(true);
        } catch (Exception e) {
            // Should only occur if the internals change.
            e.printStackTrace();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:此实现适用于 Java 8,未来版本更改了后端,从而打破了这一点。如果您确实希望继续此策略,则相同的概念仍然适用。