Java 反射—— setAccability 的影响(true)

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

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

我想另一种说明这个问题的方法是,如果我调用 setAccessible(true),在我完成之后将它设置回原始值有多重要?

90733 次浏览

getDeclaredField方法必须每次返回一个新对象,正是因为这个对象具有可变的 accessible标志。所以没有必要重置旗帜。你可以在 这篇博文中找到完整的细节。

使用 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
}


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


}

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

但是..。

如果希望对 field.setAccessible(true)的调用持久化,则需要在 java.lang.Classjava.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();
}
}
}

更新 : 这个实现是针对 Java8的,未来的版本会改变后端,从而破坏这一点。同样的概念仍然适用,但如果你真的希望继续这一战略。