引言
从这篇文章开始,我们学习反射体系中的Field、Method和Constructor这几个类,除了Class,这几个类应该是我们在反射中用的最多的了。与Class的讲解方式一样,我们先来看Field类结构层次中两个重要的类接口。
Field的类层次结构
Field的定义如下:
public final
class Field extends AccessibleObject implements Member {}
它继承了AccessibleObject类,实现了Member接口。如下图所示:
AccessibleObject接口
定义
AccessibleObject定义如下:
public class AccessibleObject implements AnnotatedElement {}
它实现了AnnotatedElement接口,所以是AccessibleObject是可以被注解的元素,注释中对AccessibleObject是这样解释的:AccessibleObject是Field、Method和Constructor的基类。如下图所示:
它提供了标识反射对象来压制Java语言的访问控制权限的能力。意思就是,通过它提供的方法,我们可以避开Java语言的访问控制校验,进而访问本来不能访问的字段、方法或者构造方法如私有方法。有一点需要注意:一个反射获得的对象默认是不能访问的。注释中这样描述:
<p>By default, a reflected object is <em>not</em> accessible.
也就是,通过反射得到的字段、方法、构造方法,无论是public还是其他的访问标志,默认都不能访问。
isAccessible()方法
这个方法返回当前这个AccessibleObject(字段、方法、构造方法)的访问标志。它直接返回的是ovverride这个成员变量,我们会在下面的方法中看到对这个变量的设置。
public boolean isAccessible() {
return override;
}
我们需要注意override这个成员变量的含义:
// Indicates whether language-level access checks are overridden
// by this object. Initializes to "false". This field is used by
// Field, Method, and Constructor.
//
// NOTE: for security purposes, this field must not be visible
// outside this package.
boolean override;
它指定的是是否要对这个AccessObject(字段、方法、构造方法)进行java语言层面的访问控制权限,并不是表示这个AccessibleObject能不能访问,能不能访问是在进行访问控制权限校验的前提下,再根据语言层面的权限修饰符例如public、private来判断的。
它的默认值是false,意思是默认所有的AccessibleObject(字段、方法、构造方法)都会进行访问控制权限校验。
看这个例子:
public class AccessTest {
private String aPrivateString;
public String aPublicString;
String aDefaultString;
protected String aProtectedString;
private void aPrivateMethod(){}
public void aPublicMethod(){}
void aDefaultMethod(){}
protected void aProtectedMethod(){}
public static void main(String[] args) {
Class<AccessTest> accessTestClass = AccessTest.class;
Field[] declaredFields = accessTestClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField.getName()+" "+declaredField.isAccessible());
}
Method[] declaredMethods = accessTestClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName() + " " + declaredMethod.isAccessible());
}
}
}
输出:
aPrivateString false
aPublicString false
aDefaultString false
aProtectedString false
main false
aPrivateMethod false
aPublicMethod false
aDefaultMethod false
aProtectedMethod false
不管是public,还是其他的访问修饰符,返回的都是false,验证了注释中的说明。意思是都要进行语言层面的访问控制权限校验。
setAccessible(boolean flag)方法
public void setAccessible(boolean flag) throws SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
setAccessible0(this, flag);
}
private static void setAccessible0(AccessibleObject obj, boolean flag)
throws SecurityException
{
if (obj instanceof Constructor && flag == true) {
Constructor<?> c = (Constructor<?>)obj;
if (c.getDeclaringClass() == Class.class) {
throw new SecurityException("Cannot make a java.lang.Class" +
" constructor accessible");
}
}
obj.override = flag;
}
这个方法设置前面提到的override,也就是设定这个AccessibleObject要不要强制进行语言层面的访问权限控制。如果是true,则不进行校验,false表示进行校验。
它是通过调用setAccessible0这个方法来设置的,setAccessible0中需要注意的是如果当前这个AccessibleObject是一个构造方法并且这个构造方法属于Class并且override要设置为true,就会抛出异常,因为我们不能将Class类的私有构造方法去掉访问控制校验,Class只能由JVM来创建。
这两个方法,我们经常会在使用反射的时候看到,当我们获取一个字段的值、设置一个字段的值或者执行一个方法时,如果这个字段或者方法不是public的,我们就得关掉访问权限控制校验,否则这些API都会抛出异常。
Member接口
定义
member接口的定义如下:
public
interface Member {}
注释中的解释如下:
Member用来反映一个member(字段field或者方法method)或者构造方法的标识信息。它的实现类如下所示:
我们来看它都能反映字段和方法的哪些信息:
方法
这里只介绍两个比较重要的方法:
public Class<?> getDeclaringClass();
这个方法返回声明这个Member(字段、方法、构造方法)的类或者接口的Class。
public String getName();
这个方法返回字符、方法或者构造方法的简单名称。
看下面的例子:
public class AccessTest {
private String aPrivateString;
public String aPublicString;
String aDefaultString;
protected String aProtectedString;
private void aPrivateMethod(){
}
public void aPublicMethod(){
}
void aDefaultMethod(){
}
protected void aProtectedMethod(){
}
public static void main(String[] args){
Class<AccessTest> accessTestClass = AccessTest.class;
Field[] declaredFields = accessTestClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField.getName()+" "+declaredField.getDeclaringClass());
}
Method[] declaredMethods = accessTestClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName()+" "+ declaredMethod.getDeclaringClass());
}
}
}
输出:
aPrivateString class person.andy.concurrency.reflect.access.AccessTest
aPublicString class person.andy.concurrency.reflect.access.AccessTest
aDefaultString class person.andy.concurrency.reflect.access.AccessTest
aProtectedString class person.andy.concurrency.reflect.access.AccessTest
main class person.andy.concurrency.reflect.access.AccessTest
aPrivateMethod class person.andy.concurrency.reflect.access.AccessTest
aPublicMethod class person.andy.concurrency.reflect.access.AccessTest
aDefaultMethod class person.andy.concurrency.reflect.access.AccessTest
aProtectedMethod class person.andy.concurrency.reflect.access.AccessTest
小结
AccessibleObject和Member都比较简单,Field在这两个的基础上扩展了很多功能,我们下一篇文章来具体分析。