引言
从这篇文章开始,我们学习反射体系中的Field、Method和Constructor这几个类,除了Class,这几个类应该是我们在反射中用的最多的了。与Class的讲解方式一样,我们先来看Field类结构层次中两个重要的类接口。
Field的类层次结构
Field的定义如下:
public finalclass 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 falseaPublicString falseaDefaultString falseaProtectedString falsemain falseaPrivateMethod falseaPublicMethod falseaDefaultMethod falseaProtectedMethod 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接口的定义如下:
publicinterface 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.AccessTestaPublicString class person.andy.concurrency.reflect.access.AccessTestaDefaultString class person.andy.concurrency.reflect.access.AccessTestaProtectedString class person.andy.concurrency.reflect.access.AccessTestmain class person.andy.concurrency.reflect.access.AccessTestaPrivateMethod class person.andy.concurrency.reflect.access.AccessTestaPublicMethod class person.andy.concurrency.reflect.access.AccessTestaDefaultMethod class person.andy.concurrency.reflect.access.AccessTestaProtectedMethod class person.andy.concurrency.reflect.access.AccessTest
小结
AccessibleObject和Member都比较简单,Field在这两个的基础上扩展了很多功能,我们下一篇文章来具体分析。
