122

我正在使用一些注释来动态设置类中字段的值。因为无论它是公共的、受保护的还是私有的,我都想这样做,所以setAccessible(true)每次在调用该set()方法之前,我都是在调用 Field 对象。setAccessible()我的问题是这个电话对球场本身有什么样的影响?

更具体地说,假设它是一个私有字段,并且这组代码调用setAccessible(true). 如果代码中的其他地方是通过反射检索相同的字段,该字段是否已经可以访问?还是getDeclaredFields()andgetDeclaredField()方法每次都返回 Field 对象的新实例?

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

4

4 回答 4

105

随着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
    }

}
于 2012-05-17T15:43:27.263 回答
37

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

于 2012-05-17T15:41:09.893 回答
4

正如其他海报所指出的那样,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();
        }
    }
}

更新:此实现适用于 Java 8,未来版本会更改破坏此的后端。如果您真的希望继续此策略,同样的概念仍然适用。

于 2018-03-12T00:42:43.707 回答
-1
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class PrivateVariableAcc {

    public static void main(String[] args) throws Exception {
        PrivateVarTest myClass = new PrivateVarTest();
        Field field1 = myClass.getClass().getDeclaredField("a");
        field1.setAccessible(true);
        System.out.println("This is access the private field-"
            + field1.get(myClass));
        Method mm = myClass.getClass().getDeclaredMethod("getA");
        mm.setAccessible(true);
        System.out.println("This is calling the private method-"
            + mm.invoke(myClass, null));
    }

}
于 2013-11-23T09:45:52.903 回答