引言

从这篇文章开始,我们学习反射体系中的Field、Method和Constructor这几个类,除了Class,这几个类应该是我们在反射中用的最多的了。与Class的讲解方式一样,我们先来看Field类结构层次中两个重要的类接口。

Field的类层次结构

Field的定义如下:

  1. public final
  2. class Field extends AccessibleObject implements Member {}

它继承了AccessibleObject类,实现了Member接口。如下图所示:
FieldInheritedStructor.png

AccessibleObject接口

定义

AccessibleObject定义如下:

  1. public class AccessibleObject implements AnnotatedElement {}

它实现了AnnotatedElement接口,所以是AccessibleObject是可以被注解的元素,注释中对AccessibleObject是这样解释的:AccessibleObject是Field、Method和Constructor的基类。如下图所示:AccessibleObject.png
它提供了标识反射对象来压制Java语言的访问控制权限的能力。意思就是,通过它提供的方法,我们可以避开Java语言的访问控制校验,进而访问本来不能访问的字段、方法或者构造方法如私有方法。有一点需要注意:一个反射获得的对象默认是不能访问的。注释中这样描述:

  1. <p>By default, a reflected object is <em>not</em> accessible.

也就是,通过反射得到的字段、方法、构造方法,无论是public还是其他的访问标志,默认都不能访问。

isAccessible()方法

这个方法返回当前这个AccessibleObject(字段、方法、构造方法)的访问标志。它直接返回的是ovverride这个成员变量,我们会在下面的方法中看到对这个变量的设置。

  1. public boolean isAccessible() {
  2. return override;
  3. }

我们需要注意override这个成员变量的含义:

  1. // Indicates whether language-level access checks are overridden
  2. // by this object. Initializes to "false". This field is used by
  3. // Field, Method, and Constructor.
  4. //
  5. // NOTE: for security purposes, this field must not be visible
  6. // outside this package.
  7. boolean override;

它指定的是是否要对这个AccessObject(字段、方法、构造方法)进行java语言层面的访问控制权限,并不是表示这个AccessibleObject能不能访问,能不能访问是在进行访问控制权限校验的前提下,再根据语言层面的权限修饰符例如public、private来判断的。
它的默认值是false,意思是默认所有的AccessibleObject(字段、方法、构造方法)都会进行访问控制权限校验。

看这个例子:

  1. public class AccessTest {
  2. private String aPrivateString;
  3. public String aPublicString;
  4. String aDefaultString;
  5. protected String aProtectedString;
  6. private void aPrivateMethod(){}
  7. public void aPublicMethod(){}
  8. void aDefaultMethod(){}
  9. protected void aProtectedMethod(){}
  10. public static void main(String[] args) {
  11. Class<AccessTest> accessTestClass = AccessTest.class;
  12. Field[] declaredFields = accessTestClass.getDeclaredFields();
  13. for (Field declaredField : declaredFields) {
  14. System.out.println(declaredField.getName()+" "+declaredField.isAccessible());
  15. }
  16. Method[] declaredMethods = accessTestClass.getDeclaredMethods();
  17. for (Method declaredMethod : declaredMethods) {
  18. System.out.println(declaredMethod.getName() + " " + declaredMethod.isAccessible());
  19. }
  20. }
  21. }

输出:

  1. aPrivateString false
  2. aPublicString false
  3. aDefaultString false
  4. aProtectedString false
  5. main false
  6. aPrivateMethod false
  7. aPublicMethod false
  8. aDefaultMethod false
  9. aProtectedMethod false

不管是public,还是其他的访问修饰符,返回的都是false,验证了注释中的说明。意思是都要进行语言层面的访问控制权限校验。

setAccessible(boolean flag)方法

  1. public void setAccessible(boolean flag) throws SecurityException {
  2. SecurityManager sm = System.getSecurityManager();
  3. if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
  4. setAccessible0(this, flag);
  5. }
  6. private static void setAccessible0(AccessibleObject obj, boolean flag)
  7. throws SecurityException
  8. {
  9. if (obj instanceof Constructor && flag == true) {
  10. Constructor<?> c = (Constructor<?>)obj;
  11. if (c.getDeclaringClass() == Class.class) {
  12. throw new SecurityException("Cannot make a java.lang.Class" +
  13. " constructor accessible");
  14. }
  15. }
  16. obj.override = flag;
  17. }

这个方法设置前面提到的override,也就是设定这个AccessibleObject要不要强制进行语言层面的访问权限控制。如果是true,则不进行校验,false表示进行校验。
它是通过调用setAccessible0这个方法来设置的,setAccessible0中需要注意的是如果当前这个AccessibleObject是一个构造方法并且这个构造方法属于Class并且override要设置为true,就会抛出异常,因为我们不能将Class类的私有构造方法去掉访问控制校验,Class只能由JVM来创建。
这两个方法,我们经常会在使用反射的时候看到,当我们获取一个字段的值、设置一个字段的值或者执行一个方法时,如果这个字段或者方法不是public的,我们就得关掉访问权限控制校验,否则这些API都会抛出异常。

Member接口

定义

member接口的定义如下:

  1. public
  2. interface Member {}

注释中的解释如下:
Member用来反映一个member(字段field或者方法method)或者构造方法的标识信息。它的实现类如下所示:
memberInherited.png
我们来看它都能反映字段和方法的哪些信息:

方法

这里只介绍两个比较重要的方法:

  1. public Class<?> getDeclaringClass();

这个方法返回声明这个Member(字段、方法、构造方法)的类或者接口的Class。

  1. public String getName();

这个方法返回字符、方法或者构造方法的简单名称。
看下面的例子:

  1. public class AccessTest {
  2. private String aPrivateString;
  3. public String aPublicString;
  4. String aDefaultString;
  5. protected String aProtectedString;
  6. private void aPrivateMethod(){
  7. }
  8. public void aPublicMethod(){
  9. }
  10. void aDefaultMethod(){
  11. }
  12. protected void aProtectedMethod(){
  13. }
  14. public static void main(String[] args){
  15. Class<AccessTest> accessTestClass = AccessTest.class;
  16. Field[] declaredFields = accessTestClass.getDeclaredFields();
  17. for (Field declaredField : declaredFields) {
  18. System.out.println(declaredField.getName()+" "+declaredField.getDeclaringClass());
  19. }
  20. Method[] declaredMethods = accessTestClass.getDeclaredMethods();
  21. for (Method declaredMethod : declaredMethods) {
  22. System.out.println(declaredMethod.getName()+" "+ declaredMethod.getDeclaringClass());
  23. }
  24. }
  25. }

输出:

  1. aPrivateString class person.andy.concurrency.reflect.access.AccessTest
  2. aPublicString class person.andy.concurrency.reflect.access.AccessTest
  3. aDefaultString class person.andy.concurrency.reflect.access.AccessTest
  4. aProtectedString class person.andy.concurrency.reflect.access.AccessTest
  5. main class person.andy.concurrency.reflect.access.AccessTest
  6. aPrivateMethod class person.andy.concurrency.reflect.access.AccessTest
  7. aPublicMethod class person.andy.concurrency.reflect.access.AccessTest
  8. aDefaultMethod class person.andy.concurrency.reflect.access.AccessTest
  9. aProtectedMethod class person.andy.concurrency.reflect.access.AccessTest

可以看到getName()返回的是方法或者字段名。

小结

AccessibleObject和Member都比较简单,Field在这两个的基础上扩展了很多功能,我们下一篇文章来具体分析。