java.lang.reflect(二)Executable

这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战


一、Executable类

抽象类Executable 位于包java.lang.reflect中,它是MethodConstructor共享超类,提供了许多展示方法信息的函数。

  • 继承了 AccessibleObject 类
  • 实现了 Member, GenericDeclaration 接口

其中,AccessibleObjectGenericDeclaration在上篇文章中简单的了解过了,而接口Member却是一个新伙伴。

二、Member接口

Member接口也是位于reflect包中,具有以下几个需要实现的方法:

  • getDeclaringClass:返回表示类或接口的 Class 对象,该类或接口声明了此 Member 表示的成员或构造函数
  • getModifiers:以整数形式返回此 Member 表示的成员或构造函数的 Java 语言修饰符
  • getName:返回由该成员表示的底层成员或构造函数的简单名称
  • isSynthetic:由编译器引入的返回true,否则返回false

定义的常量如下:

  • DECLARED:标识类或接口的声明成员集;不包括继承成员
  • PUBLIC:标识类或接口的所有公共成员的集合,包括继承的成员

终上所述,Member接口规定了实现或继承该接口的类或接口的构造函数、修饰符、名称以及来源。

三、java.lang.reflect.Modifier

Modifier类在Executable中有所涉及,所以需要简单了解一下。

该类提供static方法常量来解码类和成员访问修饰符。 修饰符集表示为整数,具有代表不同修饰符的不同位位置。 表示修饰符的常量的值取自The Java™ Virtual Machine Specification的第 4.1、4.4、4.5和 4.7 节中的表格。

公开基础修饰符:

  • ACCESS_MODIFIERS 表示访问控制修饰符
    • PUBLIC 公开的
    • PRIVATE 私有的
    • PROTECTED 受保护的
  • FINAL 最终的、不允许修改
  • INTERFACE 表示接口
  • NATIVE 本地
  • STATIC 静态
  • STRICT 表示strictfp修饰符
  • TRANSIENT 临时
  • VOLATILE 表示volatile修饰符
  • SYNCHRONIZED 同步
  • ABSTRACT 表示抽象

非公开的几个修饰符:

  • BRIDGE
  • VARARGS
  • ANNOTATION 表示注解
  • ENUM
  • MANDATED
  • SYNTHETIC

修饰符分类:

  • CLASS_MODIFIERS 可应用于类的 Java 源代码修饰符
    • ACCESS_MODIFIERS
    • ABSTRACT
    • STATIC
    • FINAL
    • STRICT
  • INTERFACE_MODIFIERS 可应用于接口的修饰符,可以看到接口比类就少了一个FINAL,因为它待实现
    • ACCESS_MODIFIERS
    • ABSTRACT
    • STATIC
    • STRICT
  • CONSTRUCTOR_MODIFIERS 可应用于构造器的修饰符
    • ACCESS_MODIFIERS
  • METHOD_MODIFIERS 可应用于方法的修饰符
    • ACCESS_MODIFIERS
    • ABSTRACT
    • STATIC
    • FINAL
    • STRICT
    • NATIVE
  • FIELD_MODIFIERS 可应用于字段的修饰符
    • ACCESS_MODIFIERS
    • STATIC
    • FINAL
    • TRANSIENT
    • VOLATILE
  • PARAMETER_MODIFIERS 可应用于方法或构造函数参数的 Java 源代码修饰符
    • FINAL

Modifier类的字段定义,我们可以一窥Java的底层风采。

在这些字段中,存在一个很少见的修饰符表示字段,那就是strictfp修饰符的表示字段STRICT。通过使用该字段,我们可以确保浮点计算独立于平台,浮点精度不由系统随意决定。

使用 strictfp 关键字声明一个方法时,该方法中所有的float和double表达式都严格遵守FP-strict的限制,符合IEEE-754规范。当对一个类或接口使用 strictfp 关键字时,该类中的所有代码,包括嵌套类型中的初始设定值和代码,都将严格地进行计算。

四、Executable的方法

接口方法

Executable类实现了以下是几个接口的方法,都没有具体的实现,应该是留给子类实现的。这种方式是名为模版模式的设计模式,便于在不同的子类中可以实现不同务逻辑。

  • Member(接口)
    • getModifiers
    • getName
    • getDeclaringClass
    • isSynthetic
  • GenericDeclaration(接口)
    • getTypeParameters
    • AnnotatedElement(父接口)
      • getAnnotation
      • getAnnotations
      • getAnnotationsByType
      • getDeclaredAnnotation
      • getDeclaredAnnotations
      • getDeclaredAnnotationsByType
      • isAnnotationPresent

模板方法模式定义了一个算法的步骤,并允许子类别为一个或多个步骤提供其实践方式。让子类在不改变算法架构的情况下,重新定义算法中的某些步骤。在软件工程中,它是一种软件设计模式,和C++模板没有关连。

其他方法

1. equalParamTypes

equalParamTypes方法用于比较两组参数的类,按照该逻辑应该是比较方法参数。

boolean equalParamTypes(Class<?>[] params1, Class<?>[] params2) {
    /* Avoid unnecessary cloning */
    if (params1.length == params2.length) {
        for (int i = 0; i < params1.length; i++) {
            if (params1[i] != params2[i])
                return false;
        }
        return true;
    }
    return false;
}
复制代码

2. parseParameterAnnotations

parseParameterAnnotations方法用于从比特数组读取注解,可以看到调用了getDeclaringClass方法获取直接注解,作为参数传递到JavaLangAccess类的getConstantPool 方法中。

Annotation[][] parseParameterAnnotations(byte[] parameterAnnotations) {
    return AnnotationParser.parseParameterAnnotations(
           parameterAnnotations,
           sun.misc.SharedSecrets.getJavaLangAccess().
           getConstantPool(getDeclaringClass()),
           getDeclaringClass());
}

复制代码

JavaLangAccess是一个接口,存在getConstantPool方法的匿名实现,实际上调用的是一个native方法。

public sun.reflect.ConstantPool getConstantPool(Class<?> klass) {
    return klass.getConstantPool();
}
复制代码

3. sharedToString

sharedToString方法用于将描述符转换为字符串,打印的数据来自于传入的参数。可以看到存在四个参数:

  • modifierMask 表示java.lang.reflect.Modifier类的整形表示
  • isDefault 表示是否是接口的默认方法
  • parameterTypes 表示参数类型的数组
  • exceptionTypes 表示异常类型的数组

相关源码如下:

String sharedToString(int modifierMask,
                      boolean isDefault,
                      Class<?>[] parameterTypes,
                      Class<?>[] exceptionTypes) {
    try {
        StringBuilder sb = new StringBuilder();

        printModifiersIfNonzero(sb, modifierMask, isDefault);
        specificToStringHeader(sb);

        sb.append('(');
        separateWithCommas(parameterTypes, sb);
        sb.append(')');
        if (exceptionTypes.length > 0) {
            sb.append(" throws ");
            separateWithCommas(exceptionTypes, sb);
        }
        return sb.toString();
    } catch (Exception e) {
        return "<" + e + ">";
    }
}

void printModifiersIfNonzero(StringBuilder sb, int mask, boolean isDefault) {
    int mod = getModifiers() & mask;

    if (mod != 0 && !isDefault) {
        sb.append(Modifier.toString(mod)).append(' ');
    } else {
        int access_mod = mod & Modifier.ACCESS_MODIFIERS;
        if (access_mod != 0)
            sb.append(Modifier.toString(access_mod)).append(' ');
        if (isDefault)
            sb.append("default ");
        mod = (mod & ~Modifier.ACCESS_MODIFIERS);
        if (mod != 0)
            sb.append(Modifier.toString(mod)).append(' ');
    }
}

void separateWithCommas(Class<?>[] types, StringBuilder sb) {
    for (int j = 0; j < types.length; j++) {
        sb.append(types[j].getTypeName());
        if (j < (types.length - 1))
            sb.append(",");
    }

}
复制代码
  • printModifiersIfNonzero方法用于打印描述符,当调用方法的modifierMask与当前类的描述符一致或者是默认方法。

    • 当修饰符一致且非默认方法,打印描述符的字符串表示;
    • 当修饰符不一致
      • 存在访问控制修饰符,则打印;
      • 如果是默认方法,则追加default字符串;
      • 如果存放非访问控制修饰符,则打印对应的修饰符,
  • 然后使用specificToStringHeader生成特定于方法或构造函数的 toString 标头信息;

  • 使用separateWithCommas方法打印所有参数的完整类名;

  • 如果存在异常类,则调用separateWithCommas方法打印所有异常类的完整类名。

4. 其他信息展示方法

在类Executable中还存在其他展示方法信息的方法:

  • sharedToGenericString 生成泛型对象相关字符串

    • getTypeParameters 按声明顺序返回一个TypeVariable对象数组,这些对象表示由此对象表示的泛型声明所声明的类型变量
    • specificToGenericStringHeader 生成特定于方法或构造函数的 toGenericString 标头信息
    • getGenericParameterTypes 返回一个Type对象数组,这些对象按声明顺序表示由此对象表示的可执行文件的形式参数类型
    • getGenericExceptionTypes 返回一个Type对象数组,这些对象表示声明为由此可执行对象抛出的异常。如果底层可执行文件在其throws子句中没有声明任何异常,则返回长度为 0 的数组
  • getName 返回由此对象表示的可执行文件的名称

  • getModifiers 返回此对象表示的可执行文件的 Java 语言修饰符

  • getParameters 返回一个Parameter对象数组,这些对象表示此对象表示的底层可执行文件的所有参数

  • getAllGenericParameterTypes 行为类似于getGenericParameterTypes ,但返回所有参数的类型信息,包括合成参数。

四、总结

Executable可以获取注解、修饰符等信息;为Method以及Constructor提供了一个信息输出通道。

猜你喜欢

转载自juejin.im/post/7032085310427103269