Java反射-setAccessible(true)的影响


105

我正在使用一些注释来动态设置类中字段的值。由于我想执行此操作,而不管它是公共的,受保护的还是私有的,因此setAccessible(true)每次调用该set()方法之前,我都会在Field对象上进行调用。我的问题是,setAccessible()电话会议对该领域本身会产生什么样的影响?

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

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

Answers:


85

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
    }

}

@PhilipRego,您需要自己编写导入声明。希望您知道该怎么做。
莫里茨·彼得森

发现问题。您必须抛出或处理NoSuchFieldException或父对象。
菲利普·雷戈

是的,这只是示例代码。我的意思是,throws Exception也可以处理NoSuchFieldException,但是您可能希望以更精细的方式进行处理。
莫里兹·彼得森

我在以下位置收到异常:Field field1 = myClass.getClass()。getDeclaredField(“ theField”); 所以它甚至不编译,即setAccessible甚至都没有关系?
user2796104

32

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


3

正如其他张贴者所指出的那样,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的,将来的版本会更改后端,从而打破了这一点。尽管您确实希望继续执行此策略,但该概念仍然适用。


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

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.