十五、反射

15.1 反射

RTTI(Run-Time Type Identification)运行时类型识别,其作用是在运行时识别一个对象的类型和类的信息。主要有两种方式:一种是传统的 RTTI,它假设我们在编译时时已经知道了所有的类型;另一种是反射机制,它允许我们在运行时发现和使用类的信息。

反射就是把 Java 类中的各种成分映射成一个个的 Java 对象。

比如说:一个类中会有成员变量、方法、构造方法、包等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。

01、Class 类

Class 类存在于 java.lang 包中,Class 类的实例表示 Java 应用运行时的类(class and enum)或者接口(interface and annotation),每个 Java 类运行时都在 JVM 里表现为一个 class 对象,可通过类名.class、类型.getClass()、Class.forName(“类名”) 等方法获取 class 对象。数组同样也被映射为 class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 class 对象。

基本类型 boolean,byte,char,short,int,long,float,double 和关键字 void 同样表现为 class 对象。

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
    private static final int ANNOTATION= 0x00002000;
    private static final int ENUM      = 0x00004000;
    private static final int SYNTHETIC = 0x00001000;

    private static native void registerNatives();
    static {
        registerNatives();
    }

    /*
     * Private constructor. Only the Java Virtual Machine creates Class objects.   //私有构造器,只有JVM才能调用创建Class对象
     * This constructor is not used and prevents the default constructor being
     * generated.
     */
    private Class(ClassLoader loader) {
        // Initialize final field for classLoader.  The initialization value of non-null
        // prevents future JIT optimizations from assuming this final field is null.
        classLoader = loader;
    }

在这个 Class 类中可以得出:

  1. Class 类也是类的一种,与 class 关键字不同;
  2. 手动编写的类被编译后会产生一个 Class 对象,其表示的是创建的类的类型信息,而且这个 Class 对象保存在 同名.class 的文件中(字节码文件);
  3. 每个通过关键字 class 标识的类,在内存中有且只有一个与之对应的 Class 对象来描述其类型信息,无论创建多少个实例对象,其依据的都是用一个 Class 对象;
  4. Class 类只存私有构造函数,因此对应 Class 对象只能由 JVM 创建和加载;
  5. Class 类的对象的作用是运行时提供或获得某个对象的类型信息,这点对于反射技术是非常重要的。

02、类加载

  1. 类加载机制流程

    在这里插入图片描述

  2. 类的加载

    在这里插入图片描述

15.2 反射的使用

在 Java 中,Class 类与 java.lang.reflect 类库一起对反射技术进行了全力的支持。

在反射包中,我们常用的类主要有 Constructor 类,表示的是 Class 对象所表示的类的构造方法,利用它可以在运行时动态创建对象;Field 表示 Class对象所表示的类的成员变量,通过它可以在运行时动态修改成员变量的属性值(包含 private);Method 表示 Class 对象所表示的类的成员方法,通过它可以动态调用对象的方法(包含 private)。

01、Class 类对象的获取

在类加载的时候,jvm 会创建一个 class 对象,class 对象是反射中最常用的,获取 class 对象的方式主要有三种:

  1. 根据类名:类名.class
  2. 根据对象:对象.getClass()
  3. 根据全限定类名:Class.forName(全限定类名)
/**
 * @author QHJ
 * @date 2022/12/22  09:48
 */
public class Test1 {
    public static void main(String[] args) throws Exception {
        // 获取 class 对象的三种方式
        System.out.println("根据类名:" + User.class); // 根据类名:class com.qhj.fanshe.User
        System.out.println("根据对象:" + new User().getClass()); // 根据对象:class com.qhj.fanshe.User
        System.out.println("根据全限定类名:" + Class.forName("com.qhj.fanshe.User")); // 根据全限定类名:class com.qhj.fanshe.User

        // 常用的方法
        System.out.println("获取全限定类名:" + User.class.getName());  // 获取全限定类名:com.qhj.fanshe.User
        System.out.println("获取类名:" + User.class.getSimpleName()); // 获取类名:User
        System.out.println("实例化:" + User.class.newInstance()); // 实例化:User{name='青花椒', age=0}
    }
}

/**
 * @author QHJ
 * @date 2022/12/22  09:47
 */
public class User {
    private String name = "青花椒";
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Class类的常用方法

方法名 功能说明
forName() 1. 获取 class 对象的一个引用,但引用的类还没有加载(该类的第一个对象没有生成)就加载了这个类;2. 为了产生 class 引用,forName() 立即就进行了初始化。
Object-getClass() 获取 class 对象的一个引用,返回表示该对象的实际类型的 class 引用
getName() 获得类的完整名字
getSimpleName() 获取类名(不包括包名)
getCanonicalName() 获取全限定的类名(包括包名)
isInterface() 判断 class 对象是否是一个接口
getInterfaces() 返回 class 对象数组,表示 class 对象所引用的类实现的所有接口
getSupercalss() 返回 class 对象,表示 class 对象所引用的类所继承的直接基类。应用该方法可在运行时发现一个对象完整的继承结构。
newInstance() 返回一个 Object 对象,是实现"虚拟构造器"的一种途径。使用该方法创建的类,必须有无参的构造器。
getFields() 获得某个类的所有的公共(public)的字段,包括继承自父类的所有公共字段。类似的还有 getMethods 和 getConstructors。
getDeclaredFields() 获得某个类的自己声明的字段,即包括 public、private 和 protected,默认是不包括父类声明的任何字段。类似的还有 getDeclaredMethods() 和 getDeclaredConstructors()。
/**
 * @author QHJ
 * @date 2022/12/22  11:06
 */
public interface Person {
}

/**
 * @author QHJ
 * @date 2022/12/22  09:47
 */
public class User {
    public String name;
    private int age;
}

/**
 * @author QHJ
 * @date 2022/12/22  11:07
 */
public class Teacher extends User implements Person{
    public int workAge;
    private double salary;
}

/**
 * @author QHJ
 * @date 2022/12/22  11:08
 */
public class Test2 {
    public static void main(String[] args) throws Exception {
        Class<Teacher> teacher = Teacher.class;
        // 类名打印
        System.out.println(teacher.getName()); // com.qhj.fanshe.Teacher
        System.out.println(teacher.getSimpleName()); // Teacher
        System.out.println(teacher.getCanonicalName()); // com.qhj.fanshe.Teacher

        // 接口
        System.out.println(teacher.isInterface()); // false  teacher不是接口
        for (Class i : teacher.getInterfaces()) {
            System.out.println(i); // interface com.qhj.fanshe.Person  teacher对象所引用的接口
        }

        // 父类
        System.out.println(teacher.getSuperclass()); // class com.qhj.fanshe.User

        // 创建对象
        Teacher teacher1 = teacher.newInstance();
        // 字段
        for (Field f : teacher.getFields()) {
            System.out.println(f.getName()); // workAge name 只打印访问修饰符为public的字段

        }

        System.out.println("---------");
        for (Field f : teacher.getDeclaredFields()) {
            System.out.println(f.getName()); // workAge salary  只打印自己类中声明的字段
        }
    }
}

getName()、getCanonicalName() 与 getSimpleName() 的区别:

  • getName():只获取类名;
  • getCanonicalName():类的全限定名,jvm 中 class 的表示,可以用于动态加载 class 对象,比如:Class.forName(“”);
  • getSimpleName():返回更容易理解的表示,主要用于输出(toString)或者 log 打印,大多数情况下和 getName() 一样,但是在内部类、数组等类型的表示形式不同。
/**
 * @author qiaohaojie
 * @date 2022/12/22  13:46
 */
public class Test3 {
    private class inner{

    }

    public static void main(String[] args) {
        // 普通类
        System.out.println(Test3.class.getName()); // com.qhj.fanshe.Test3
        System.out.println(Test3.class.getSimpleName()); // Test3
        System.out.println(Test3.class.getCanonicalName()); // com.qhj.fanshe.Test3
        // 内部类
        System.out.println(inner.class.getName()); // com.qhj.fanshe.Test3$inner
        System.out.println(inner.class.getSimpleName());
        System.out.println(inner.class.getCanonicalName());
        // 数组
        System.out.println(args.getClass().getName()); // [Ljava.lang.String;
        System.out.println(args.getClass().getSimpleName()); // String[]
        System.out.println(args.getClass().getCanonicalName()); // java.lang.String[]

        // 不能使用getCanonicalName()去加载类对象,必须使用getName()
        try {
            Class.forName(inner.class.getCanonicalName()); // 编译失败,报错:java.lang.ClassNotFoundException: com.qhj.fanshe.Test3.inner
            Class.forName(inner.class.getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

02、Constructor 类及其用法

Constructor 类存在于反射包(java.lang.reflect)中,反映的是 class 对象所表示的类的构造方法。

获取 Constructor 对象是通过 class 类中的方法获取的,其中,Class 类与 Constructor 相关的方法主要有以下几个:

方法返回值 方法名称 说明
static Class<?> forName(String className) 返回与带有给定字符串名的类或接口相关联的class对象
Constructor getConstructor(Class<?>… parameterTypes) 返回指定参数类型、具有public访问权限的构造函数对象
Constructor<?>[] getConstructors() 返回所有具有public访问权限的构造函数的Constructor对象数组
Constructor getDeclaredConstructor(Class<?>… parameterTypes) 返回指定参数类型、所有声明(包括private)构造函数对象
Constructor<?>[] getDeclaredConstructors() 返回所有声明的(包括private)构造函数对象
T newInstance() 调用无参构造器创建此class对象所表示的类的一个新实例
/**
 * @author QHJ
 * @date 2022/12/22  09:47
 */
public class User {
    private String name;
    private int age;

    public User() {
    }

    public User(String name) {
        this.name = name;
    }

    /**
     * 私有构造函数
     *
     * @param name
     * @param age
     */
    private User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
/**
 * @author QHJ
 * @date 2022/12/26  17:20
 */
public class Test4 implements Serializable {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = null;

        // 获取Class对象的引用
        clazz = Class.forName("com.qhj.fanshe.User");

        // 实例化默认构造方法,User必须是无参构造函数,否则将抛出异常
        User user = (User) clazz.newInstance();
        user.setAge(23);
        user.setName("青花椒");
        System.out.println(user.toString()); // User{name='青花椒', age=23}

        // 获取带有String参数的public构造方法
        Constructor<?> constructor = clazz.getConstructor(String.class);
        // 创建User
        User user1 = (User) constructor.newInstance("青花椒青花椒");
        user1.setAge(32);
        System.out.println("user1:" + user1.toString()); // user1:User{name='青花椒青花椒', age=32}

        // 获取指定带有int和String参数的私有构造方法
        Constructor<?> constructor1 = clazz.getDeclaredConstructor(String.class, int.class);
        // private类型必须设置可访问
        constructor1.setAccessible(true);
        User user2 = (User) constructor1.newInstance("青花椒", 23);
        System.out.println("user2:" + user2.toString()); // user2:User{name='青花椒', age=23}

        // 获取所有构造函数,包括orivate
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        // 查看每个构造方法需要的参数
        for (int i = 0; i < constructors.length; i++) {
            // 获取构造函数类型
            Class<?>[] parameterTypes = constructors[i].getParameterTypes();
            System.out.println("构造函数[" + i + "]" + constructors[i].toString());
            System.out.println("参数类型[" + i + "]:(");
            for (int j = 0; j < parameterTypes.length; j++) {
                if (j == parameterTypes.length - 1) {
                    System.out.print(parameterTypes[j].getName());
                } else {
                    System.out.print(parameterTypes[j].getName() + ",");
                }
            }
            System.out.println(")");
        }
        /**
         * 构造函数[0]private com.qhj.fanshe.User(java.lang.String,int)
         * 参数类型[0]:(java.lang.String,int)
         * 构造函数[1]public com.qhj.fanshe.User(java.lang.String)
         * 参数类型[1]:(java.lang.String)
         * 构造函数[2]public com.qhj.fanshe.User()
         * 参数类型[2]:()
         */
    }
}

关于 Constructor 类本身也有一些常用的方法:

方法返回值 方法名称 说明
Class getDeclaringClass() 返回Class对象,该对象表示声明由此Constructor对象表示的构造方法的类,其实就是返回真实类型(不包含参数)
Type[] getGenericParameterTypes() 按照声明顺序返回一组Type对象,返回的就是Constructor对象构造函数的形参类型
String getName() 以字符串形式返回此构造方法的名称
Class<?>[] getParameterTypes() 按照声明顺序返回一组Class对象,即返回Constructor对象所表示构造方法的形参类型
T newInstance(Object… initargs) 使用此Constructor对象表示的构造函数来创建新实例
String toGenericString() 返回描述此Constructor的字符串,其中包括类型参数
/**
 * @author QHJ
 * @date 2022/12/26  20:13
 */
public class Test5 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = null;

        // 获取Class对象的引用
        clazz = Class.forName("com.qhj.fanshe.User");
        Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);

        System.out.println("getDeclaringClass()");
        Class c1 = constructor.getDeclaringClass();
        // Constructor对象表示的构造方法的类
        System.out.println("构造方法的类:" + c1.getName()); // 构造方法的类:com.qhj.fanshe.User


        System.out.println("getGenericParameterTypes()");
        // 表示此Constructor对象所表示的方法的形参类型
        Type[] types = constructor.getGenericParameterTypes();
        for (Type type : types) {
            /**
             * 参数名称type:class java.lang.String
             * 参数名称type:int
             */
            System.out.println("参数名称type:" + type);
        }

        System.out.println("getParameterTypes()");
        // 获取构造函数的参数类型
        Class<?>[] clazzs = constructor.getParameterTypes();
        for (Class claz : clazzs) {
            /**
             * 参数名称:java.lang.String
             * 参数名称:int
             */
            System.out.println("参数名称:" + claz.getName());
        }

        System.out.println("getName()");
        // 以字符串形式返回此构造方法的名称
        System.out.println("getName:" + constructor.getName()); // getName:com.qhj.fanshe.User

        System.out.println("toGenericString()");
        // 返回描述此Constructor的字符串,其中包括类型参数
        System.out.println("toGenericString:" + constructor.toGenericString()); // toGenericString:private com.qhj.fanshe.User(java.lang.String,int)
    }
}

03、Field 类及其用法

Field 提供有关类或者接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。

我们可以通过 Class 类提供的方法来获取代表字段信息的 Field 对象,Class 类与 Field 对象相关的一些方法:

方法返回值 方法名称 说明
Field getDeclaredField(String name) 获取指定name名称的(包含private修饰的)字段,不包括继承的字段
Field[] getDeclaredFields() 获取Class对象所表示的类或接口的所有(包含private修饰的)字段,不包括继承的字段
Field getField(String name) 获取指定name名称、具有public修饰的字段,包含继承字段
Field[] getFields() 获取修饰符为public的字段,包含继承字段
/**
 * @author QHJ
 * @date 2022/12/27  09:39
 */
public class Worker {
    public int age;
    public String name;
}

/**
 * @author QHJ
 * @date 2022/12/27  09:40
 */
public class Employee extends Worker{
    public Double salary;
    private int workAge;
}
/**
 * @author QHJ
 * @date 2022/12/27  09:43
 */
public class Test6 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.qhj.fanshe.Employee");

        // 获取指定字段名称的Field类,其中字段修饰符必须是public并且存在该字段
        // 否则抛出异常
        Field field = clazz.getField("age");
        System.out.println("field:" + field); // field:public int com.qhj.fanshe.Worker.age

        // 获取所有修饰符为public的字段,包含父类字段
        Field fields[] = clazz.getFields();
        for (Field f : fields) {
            /**
             * f:salary
             * f:age
             * f:name
             */
            System.out.println("f:" + f.getName());
        }

        // 获取当前类所有字段(包含private字段),不包含父类字段
        Field fields2[] = clazz.getDeclaredFields();
        for (Field f : fields2) {
            /**
             * f2:salary
             * f2:workAge
             */
            System.out.println("f2:" + f.getName());
        }

        // 获取指定字段名称的Field类,可以是任意修饰符,但是不包含父类的字段
        Field field2 = clazz.getDeclaredField("workAge");
        System.out.println("field2:" + field2); // field2:private int com.qhj.fanshe.Employee.workAge
    }
}

如果我们不需要获取其父类,就使用 Class 类的 getDeclaredField(String name)、getDeclaredFields() 方法来获取字段即可。如果需要连带获取到父类的字段,就要使用 Class 类的 getField(String name)、getFields() 方法了,但是也只能获取到 public 修饰的字段,无法获取父类的私有字段。

通过 Field 类本身的方法对指定类属性赋值:

/**
 * @author QHJ
 * @date 2022/12/27  10:15
 */
public class Test7 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.qhj.fanshe.Employee");

        Employee employee = (Employee) clazz.newInstance();
        // 获取父类public字段并赋值
        Field nameField = clazz.getField("name");
        nameField.set(employee, "青花椒");
        Field ageField = clazz.getField("age");
        ageField.set(employee, 23);
        System.out.println(employee.toString()); // Employee{salary=null, workAge=0}

        // 只获取当前类的字段,不获取父类的字段
        Field salaryField = clazz.getDeclaredField("salary");
        salaryField.set(employee, 12.3);
        Field workAgeField = clazz.getDeclaredField("workAge");
        // 设置可访问权限
        workAgeField.setAccessible(true);
        workAgeField.set(employee, 2);
        System.out.println(employee.toString()); // Employee{salary=12.3, workAge=2}
    }
}

其中,set(Object obj, Object value) 方法是 Field 类本身的方法,用于设置字段的值,而 get(Object obj) 方法则是获取字段的值。关于 Field 类还有其他常用的方法:

方法返回值 方法名称 说明
void set(Object obj, Object value) 将指定对象变量上次此Field对象表示的字段设置为指定的新值
Object get(Object obj) 返回指定对象上此Field表示的字段的值
Class<?> getType() 返回一个Class对象,它标识了此Field对象所表示字段的声明类型
boolean isEnumConstant() 如果此字段表示枚举类型的元素,则返回true;否则返回false
String toGenericString() 返回一个描述此Field(包括其一般类型)的字符串
String getName() 返回此Field对象表示的字段的名称
Class<?> getDeclaringClass() 返回表示类或接口的Class对象,该类或接口声明由此Field对象表示的字段
void setAccessible(boolean flag) 将此对象的accessible标志设置为指示的布尔值,即设置其可访问性

事实上,在设置值的方法上,Field 类还提供了专门针对基本数据类型的方法,比如:setInt()/getInt()、setBoolean()/getBoolean()、setChar()/getChar() 等等。需要特别注意的是,被 final 关键字修饰的 Field字段是安全的,在运行时可以接收任何修改,但最终其实际值是不会发生改变的。

04、Method 类及其用法

Method 提供关于类或接口上单独某个方法(一级如何访问该方法)的信息,所反映的方法可能是类方法或实例方法(包括抽象方法)。

Class 类获取 Method 对象相关的方法:

方法返回值 方法名称 方法说明
Method getDeclaredMethod(String name, Class<?>… parameterTypes) 返回一个指定参数的Method对象,该对象反映此Class对象所表示的类或接口的指定已声明方法
Method[] getDeclaredMethods() 返回Method对象的一个数组,这些对象反映此Class对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,单不包括继承的方法
Method getMethod(String name, Class<?>… parameterTypes) 返回一个Method对象,它反映此Class对象所表示的类或接口的指定公共成员方法
Method[] getMethods() 返回一个包含某些Method对象的数组,这些对象反映此Class对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些类或接口)的公共member方法
/**
 * @author QHJ
 * @date 2022/12/27  15:18
 */
public class Shape {
    public void draw() {
        System.out.println("draw");
    }

    public void draw(int count, String name) {
        System.out.println("draw" + name + ",count=" + count);
    }
}

/**
 * @author QHJ
 * @date 2022/12/27  15:19
 */
public class Circle extends Shape {
    private void drawCircle() {
        System.out.println("drawCircle");
    }

    public int getAllCount() {
        return 100;
    }
}
/**
 * @author QHJ
 * @date 2022/12/27  15:20
 */
public class Test8 {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.qhj.fanshe.Circle");

        // 根据参数获取public的Method,包含继承自父类的方法
        Method method = clazz.getMethod("draw", int.class, String.class);
        System.out.println("method:" + method); // method:public void com.qhj.fanshe.Shape.draw(int,java.lang.String)

        // 获取所有public的方法
        Method[] methods = clazz.getMethods();
        for (Method m : methods) {
            System.out.println("m:" + m);
        }

        // 获取当前类的方法,包含private,该方法无法获取继承自父类的method
        Method method1 = clazz.getDeclaredMethod("drawCircle");
        System.out.println("method1:" + method1); // method1:private void com.qhj.fanshe.Circle.drawCircle()

        // 获取当前类的所有方法,包含private,该方法无法获取继承自父类的method
        Method[] methods1 = clazz.getDeclaredMethods();
        for (Method m : methods1) {
            /**
             * m1:public int com.qhj.fanshe.Circle.getAllCount()
             * m1:private void com.qhj.fanshe.Circle.drawCircle()
             */
            System.out.println("m1:" + m);
        }
    }
}

在通过 getMethods() 方法获取 Method 对象时,会把父类的方法也获取到,上面的输出中,把 Object 类的方法都打印出来了。而 getDeclaredMethod()/getDeclaredMethods() 方法都只能获取当前类的方法。下面是通过 Method 对象调用指定类的方法:

/**
 * @author QHJ
 * @date 2022/12/27  16:32
 */
public class Test9 {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.qhj.fanshe.Circle");

        // 创建对象
        Circle circle = (Circle) clazz.newInstance();

        // 获取指定参数的方法对象Method
        Method method = clazz.getMethod("draw", int.class, String.class);

        // 通过Method对象的invoke(Object obj, Object... args)方法调用
        method.invoke(circle, 15, "圆圈"); // method1.invoke(circle);

        // 对私有无参方法的操作
        Method method1 = clazz.getDeclaredMethod("drawCircle");
        // 修改私有方法的访问标识
        method1.setAccessible(true);
        method1.invoke(circle); // drawCircle

        // 对有返回值的方法操作
        Method method2 = clazz.getMethod("getAllCount");
        Integer count = (Integer) method2.invoke(circle);
        System.out.println("count:" + count); // count:100
    }
}

Method 类的 invoke(Object obj, Object… args) 第一个参数代表调用的对象,第二个参数传递的调用方法的参数。这样就完成了类方法的动态调用。

方法返回值 方法名称 说明
Object invoke(Object obj, Object… args) 对带有指定参数的指定对象调用由此Method对象表示的底层方法
Class<?> getReturnType() 返回一个Class对象,该对象描述了此Method对象所表示的方法的正式返回类型,即方法的返回类型
Type getGenericReturnType() 返回表示由此Method对象所表示方法的正式返回类型的Type对象,也是方法的返回类型
Class<?>[] getParameterTypes() 按照声明顺序返回Class对象的数组,这些对象描述了此Method对象所表示的方法的形参类型。即返回方法的参数类型组成的数组
Type[] getGenericParameterTypes() 按照声明顺序返回Type对象的数组,这些对象描述了此Method对象所表示的方法的形参类型的,也是返回方法的参数类型
String getName() 以String形式返回此Method对象表示的方法名称,即返回方法的名称
boolean isVarArgs() 判断方法是否带可变参数,如果将此方法声明为带有可变数量的参数,则返回true;否则,返回false
String toGenericString() 返回描述此Method的字符串,包括类型参数

15.3 反射机制执行流程

public class HelloReflect {
    public static void main(String[] args) {
        try {
            // 1. 使用外部配置的实现,进行动态加载类
            TempFunctionTest test = (TempFunctionTest)Class.forName("com.tester.HelloReflect").newInstance();
            test.sayHello("call directly");
            // 2. 根据配置的函数名,进行方法调用(不需要通用的接口抽象)
            Object t2 = new TempFunctionTest();
            Method method = t2.getClass().getDeclaredMethod("sayHello", String.class);
            method.invoke(test, "method invoke");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e ) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    
    public void sayHello(String word) {
        System.out.println("hello," + word);
    }
}

在这里插入图片描述

01、反射获取类实例

首先调用了 java.lang.Class 的静态方法获取类信息:

@CallerSensitive
public static Class<?> forName(String className)
            throws ClassNotFoundException {
    // 先通过反射,获取调用进来的类信息,从而获取当前的 classLoader
    Class<?> caller = Reflection.getCallerClass();
    // 调用native方法进行获取class信息
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

forName() 反射获取类信息,并没有将实现留给 Java,而是交给了 jvm 去加载。

主要是先获取 ClassLoader,然后调用 native 方法获取信息,加载类则是回调 java.lang.ClassLoader。

最后,jvm 又会回调 ClassLoader 进行类加载。

public Class<?> loadClass(String name) throws ClassNotFoundException {
	return loadClass(name, false);
}
	 
// sun.misc.Launcher
public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
    int var3 = var1.lastIndexOf(46);
    if(var3 != -1) {
        SecurityManager var4 = System.getSecurityManager();
        if(var4 != null) {
            var4.checkPackageAccess(var1.substring(0, var3));
        }
    }

    if(this.ucp.knownToNotExist(var1)) {
        Class var5 = this.findLoadedClass(var1);
        if(var5 != null) {
            if(var2) {
                this.resolveClass(var5);
            }

            return var5;
        } else {
            throw new ClassNotFoundException(var1);
        }
    } else {
        return super.loadClass(var1, var2);
    }
}
// java.lang.ClassLoader
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    // 先获取锁
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        // 如果已经加载了的话,就不用再加载了
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                // 双亲委托加载
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            // 父类没有加载到时,再自己加载
            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

protected Object getClassLoadingLock(String className) {
    Object lock = this;
    if (parallelLockMap != null) {
        // 使用 ConcurrentHashMap来保存锁
        Object newLock = new Object();
        lock = parallelLockMap.putIfAbsent(className, newLock);
        if (lock == null) {
            lock = newLock;
        }
    }
    return lock;
}

protected final Class<?> findLoadedClass(String name) {
    if (!checkName(name))
        return null;
    return findLoadedClass0(name);
}

再来看一下 newInstance() 的实现方式:

// 首先肯定是 Class.newInstance
@CallerSensitive
public T newInstance()
    throws InstantiationException, IllegalAccessException
{
    if (System.getSecurityManager() != null) {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
    }

    // NOTE: the following code may not be strictly correct under
    // the current Java memory model.

    // Constructor lookup
    // newInstance() 其实相当于调用类的无参构造函数,所以,首先要找到其无参构造器
    if (cachedConstructor == null) {
        if (this == Class.class) {
            // 不允许调用 Class 的 newInstance() 方法
            throw new IllegalAccessException(
                "Can not call newInstance() on the Class for java.lang.Class"
            );
        }
        try {
            // 获取无参构造器
            Class<?>[] empty = {};
            final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
            // Disable accessibility checks on the constructor
            // since we have to do the security check here anyway
            // (the stack depth is wrong for the Constructor's
            // security check to work)
            java.security.AccessController.doPrivileged(
                new java.security.PrivilegedAction<Void>() {
                    public Void run() {
                            c.setAccessible(true);
                            return null;
                        }
                    });
            cachedConstructor = c;
        } catch (NoSuchMethodException e) {
            throw (InstantiationException)
                new InstantiationException(getName()).initCause(e);
        }
    }
    Constructor<T> tmpConstructor = cachedConstructor;
    // Security check (same as in java.lang.reflect.Constructor)
    int modifiers = tmpConstructor.getModifiers();
    if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
        Class<?> caller = Reflection.getCallerClass();
        if (newInstanceCallerCache != caller) {
            Reflection.ensureMemberAccess(caller, this, null, modifiers);
            newInstanceCallerCache = caller;
        }
    }
    // Run constructor
    try {
        // 调用无参构造器
        return tmpConstructor.newInstance((Object[])null);
    } catch (InvocationTargetException e) {
        Unsafe.getUnsafe().throwException(e.getTargetException());
        // Not reached
        return null;
    }
}

newInstance() 主要做了三件事:

  1. 权限检测,如果不通过直接抛出异常;
  2. 查找无参构造器,并将其缓存起来;
  3. 调用具体方法的无参构造方法,生成实例并返回。

下面是获取构造器的过程:

private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
                                        int which) throws NoSuchMethodException
{
    // 获取所有构造器
    Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
    for (Constructor<T> constructor : constructors) {
        if (arrayContentsEq(parameterTypes,
                            constructor.getParameterTypes())) {
            return getReflectionFactory().copyConstructor(constructor);
        }
    }
    throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
}

getConstructor0() 为获取匹配的构造方法,分为三步:

  1. 先获取所有的 constructors,然后对参数类型进行比较;
  2. 找到匹配后,通过 ReflectionFactory copy 一份 constructor 返回;
  3. 否则抛出 NoSuchMethodException。

privateGetDeclaredConstructors(),是获取所有的构造器,主要步骤有:

  1. 先尝试从缓存中获取;
  2. 如果缓存没有,就从 jvm 中重新获取,并存入缓存,缓存使用软引用进行保存,保证内存可用。
// 获取当前类所有的构造方法,通过jvm或者缓存
// Returns an array of "root" constructors. These Constructor
// objects must NOT be propagated to the outside world, but must
// instead be copied via ReflectionFactory.copyConstructor.
private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {
    checkInitted();
    Constructor<T>[] res;
    // 调用 reflectionData(), 获取保存的信息,使用软引用保存,从而使内存不够可以回收
    ReflectionData<T> rd = reflectionData();
    if (rd != null) {
        res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
        // 存在缓存,则直接返回
        if (res != null) return res;
    }
    // No cached value available; request value from VM
    if (isInterface()) {
        @SuppressWarnings("unchecked")
        Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];
        res = temporaryRes;
    } else {
        // 使用native方法从jvm获取构造器
        res = getDeclaredConstructors0(publicOnly);
    }
    if (rd != null) {
        // 最后,将从jvm中读取的内容,存入缓存
        if (publicOnly) {
            rd.publicConstructors = res;
        } else {
            rd.declaredConstructors = res;
        }
    }
    return res;
}

// Lazily create and cache ReflectionData
private ReflectionData<T> reflectionData() {
    SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
    int classRedefinedCount = this.classRedefinedCount;
    ReflectionData<T> rd;
    if (useCaches &&
        reflectionData != null &&
        (rd = reflectionData.get()) != null &&
        rd.redefinedCount == classRedefinedCount) {
        return rd;
    }
    // else no SoftReference or cleared SoftReference or stale ReflectionData
    // -> create and replace new instance
    return newReflectionData(reflectionData, classRedefinedCount);
}

// 新创建缓存,保存反射信息
private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData,
                                            int classRedefinedCount) {
    if (!useCaches) return null;

    // 使用cas保证更新的线程安全性,所以反射是保证线程安全的
    while (true) {
        ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount);
        // try to CAS it...
        if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) {
            return rd;
        }
        // 先使用CAS更新,如果更新成功,则立即返回,否则测查当前已被其他线程更新的情况,如果和自己想要更新的状态一致,则也算是成功了
        oldReflectionData = this.reflectionData;
        classRedefinedCount = this.classRedefinedCount;
        if (oldReflectionData != null &&
            (rd = oldReflectionData.get()) != null &&
            rd.redefinedCount == classRedefinedCount) {
            return rd;
        }
    }
}

另外,使用 relationDate() 进行缓存保存,ReflectionDate 的数据结构:

// reflection data that might get invalidated when JVM TI RedefineClasses() is called
private static class ReflectionData<T> {
    volatile Field[] declaredFields;
    volatile Field[] publicFields;
    volatile Method[] declaredMethods;
    volatile Method[] publicMethods;
    volatile Constructor<T>[] declaredConstructors;
    volatile Constructor<T>[] publicConstructors;
    // Intermediate results for getFields and getMethods
    volatile Field[] declaredPublicFields;
    volatile Method[] declaredPublicMethods;
    volatile Class<?>[] interfaces;

    // Value of classRedefinedCount when we created this ReflectionData instance
    final int redefinedCount;

    ReflectionData(int redefinedCount) {
        this.redefinedCount = redefinedCount;
    }
}

其中,还有一个点,就是如何比较构造是否要查找构造器,其实就是比较类型完全相等就完了,有一个不相等就返回 false。

private static boolean arrayContentsEq(Object[] a1, Object[] a2) {
    if (a1 == null) {
        return a2 == null || a2.length == 0;
    }

    if (a2 == null) {
        return a1.length == 0;
    }

    if (a1.length != a2.length) {
        return false;
    }

    for (int i = 0; i < a1.length; i++) {
        if (a1[i] != a2[i]) {
            return false;
        }
    }

    return true;
}
// sun.reflect.ReflectionFactory
/** Makes a copy of the passed constructor. The returned
    constructor is a "child" of the passed one; see the comments
    in Constructor.java for details. */
public <T> Constructor<T> copyConstructor(Constructor<T> arg) {
    return langReflectAccess().copyConstructor(arg);
}

// java.lang.reflect.Constructor, copy 其实就是新new一个 Constructor 出来
Constructor<T> copy() {
    // This routine enables sharing of ConstructorAccessor objects
    // among Constructor objects which refer to the same underlying
    // method in the VM. (All of this contortion is only necessary
    // because of the "accessibility" bit in AccessibleObject,
    // which implicitly requires that new java.lang.reflect
    // objects be fabricated for each reflective call on Class
    // objects.)
    if (this.root != null)
        throw new IllegalArgumentException("Can not copy a non-root Constructor");

    Constructor<T> res = new Constructor<>(clazz,
                                           parameterTypes,
                                           exceptionTypes, modifiers, slot,
                                           signature,
                                           annotations,
                                           parameterAnnotations);
    // root 指向当前 constructor
    res.root = this;
    // Might as well eagerly propagate this if already present
    res.constructorAccessor = constructorAccessor;
    return res;
}

通过上面,获取到 Constructor 了。

接下来就只需要调用其相应构造器的 newInstance(),即返回实例了。

// return tmpConstructor.newInstance((Object[])null); 
// java.lang.reflect.Constructor
@CallerSensitive
public T newInstance(Object ... initargs)
    throws InstantiationException, IllegalAccessException,
           IllegalArgumentException, InvocationTargetException
{
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, null, modifiers);
        }
    }
    if ((clazz.getModifiers() & Modifier.ENUM) != 0)
        throw new IllegalArgumentException("Cannot reflectively create enum objects");
    ConstructorAccessor ca = constructorAccessor;   // read volatile
    if (ca == null) {
        ca = acquireConstructorAccessor();
    }
    @SuppressWarnings("unchecked")
    T inst = (T) ca.newInstance(initargs);
    return inst;
}
// sun.reflect.DelegatingConstructorAccessorImpl
public Object newInstance(Object[] args)
  throws InstantiationException,
         IllegalArgumentException,
         InvocationTargetException
{
    return delegate.newInstance(args);
}
// sun.reflect.NativeConstructorAccessorImpl
public Object newInstance(Object[] args)
    throws InstantiationException,
           IllegalArgumentException,
           InvocationTargetException
{
    // We can't inflate a constructor belonging to a vm-anonymous class
    // because that kind of class can't be referred to by name, hence can't
    // be found from the generated bytecode.
    if (++numInvocations > ReflectionFactory.inflationThreshold()
            && !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) {
        ConstructorAccessorImpl acc = (ConstructorAccessorImpl)
            new MethodAccessorGenerator().
                generateConstructor(c.getDeclaringClass(),
                                    c.getParameterTypes(),
                                    c.getExceptionTypes(),
                                    c.getModifiers());
        parent.setDelegate(acc);
    }

    // 调用native方法,进行调用 constructor
    return newInstance0(c, args);
}

返回构造器的实例后,可以根据外部进行类型转换,从而使用接口或方法进行调用实例功能了。

02、反射获取方法

  • 第一步,先获取 Method。

    // java.lang.Class
    @CallerSensitive
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }
    

    忽略第一个检查权限,剩下的就只有两个动作了:

    1. 获取所有方法列表;
    2. 根据方法名称和方法列表,选出符合要求的方法;
    3. 如果没有找到相应的方法,抛出异常,否则返回对应方法。

    所以,要先看一下如何获取类声明的所有方法:

     // Returns an array of "root" methods. These Method objects must NOT
     // be propagated to the outside world, but must instead be copied
     // via ReflectionFactory.copyMethod.
     private Method[] privateGetDeclaredMethods(boolean publicOnly) {
         checkInitted();
         Method[] res;
         ReflectionData<T> rd = reflectionData();
         if (rd != null) {
             res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
             if (res != null) return res;
         }
         // No cached value available; request value from VM
         res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
         if (rd != null) {
             if (publicOnly) {
                 rd.declaredPublicMethods = res;
             } else {
                 rd.declaredMethods = res;
             }
         }
         return res;
     }
    

    很相似,和获取所有构造器的方法很相似,都是先从缓存中获取方法,如果没有,就从 jvm 中获取。

    不同的是,方法列表需要进行过滤 Reflection.filterMethods,但是这个方法我们一般不会派上用场:

    // sun.misc.Reflection
    public static Method[] filterMethods(Class<?> containingClass, Method[] methods) {
        if (methodFilterMap == null) {
            // Bootstrapping
            return methods;
        }
        return (Method[])filter(methods, methodFilterMap.get(containingClass));
    }
    // 可以过滤指定的方法,一般为空,如果要指定过滤,可以调用 registerMethodsToFilter(), 或者...
    private static Member[] filter(Member[] members, String[] filteredNames) {
        if ((filteredNames == null) || (members.length == 0)) {
            return members;
        }
        int numNewMembers = 0;
        for (Member member : members) {
            boolean shouldSkip = false;
            for (String filteredName : filteredNames) {
                if (member.getName() == filteredName) {
                    shouldSkip = true;
                    break;
                }
            }
            if (!shouldSkip) {
                ++numNewMembers;
            }
        }
        Member[] newMembers =
            (Member[])Array.newInstance(members[0].getClass(), numNewMembers);
        int destIdx = 0;
        for (Member member : members) {
            boolean shouldSkip = false;
            for (String filteredName : filteredNames) {
                if (member.getName() == filteredName) {
                    shouldSkip = true;
                    break;
                }
            }
            if (!shouldSkip) {
                newMembers[destIdx++] = member;
            }
        }
        return newMembers;
    }
    
  • 第二步,根据方法名和参数类型过滤指定方法返回。

    private static Method searchMethods(Method[] methods,
                                            String name,
                                            Class<?>[] parameterTypes)
    {
        Method res = null;
        // 使用常量池,避免重复创建String
        String internedName = name.intern();
        for (int i = 0; i < methods.length; i++) {
            Method m = methods[i];
            if (m.getName() == internedName
                && arrayContentsEq(parameterTypes, m.getParameterTypes())
                && (res == null
                    || res.getReturnType().isAssignableFrom(m.getReturnType())))
                res = m;
        }
    
        return (res == null ? res : getReflectionFactory().copyMethod(res));
    }
    

    大概意思明白了,就是匹配到方法名,然后参数类型匹配才可以。但是有几点需要注意:

    1. 匹配到一个方法,并没有退出 for 循环,而是继续进行匹配;
    2. 这里是匹配最精确的子类进行返回(最优匹配);
    3. 最后,还是通过 ReflectionFactory、copy 方法后返回。

03、调用 method.invoke() 方法

方法的反射调用,最终是由 Method 对象的 invoke() 方法完成的,扒一下源码(JDK 1.8):

@CallerSensitive
public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
        InvocationTargetException
{
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    MethodAccessor ma = methodAccessor;             // read volatile
    if (ma == null) {
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
}

两个嵌套的 if 语句时用来进行权限检查的。

invoke() 方法实际上是委派给 MethodAccessor 接口来完成的:
JavaSE进阶之(十五)反射-小白菜博客
MethodAccessor 接口有三个实现类,其中的 MethodAccessorImpl 是一个抽象类,另外两个具体的实现类继承了这个抽象类:
在这里插入图片描述

  • NativeMethodAccessorImpl:通过本地方法来实现反射调用;
  • DelegatingMethodAccessorImpl:通过委派模式来实现反射调用。

通过 debug 的方式进入 invoke() 方法后,可以看到第一次反射调用会生成一个委派实现 DelegatingMethodAccessorImpl。它在生成的时候会传递一个本地实现 NativeMethodAccessorImpl:
JavaSE进阶之(十五)反射-小白菜博客
也就是说,invoke() 方法在执行的时候,会先调用 DelegatingMethodAccessorImpl,然后调用 NativeMethodAccessorImpl,最后再调用实际的方法。

为什么不直接调用本地实现呢?

之所以采用委派实现,是为了能够在本地实现和动态实现之间切换。动态实现是另外一种反射调用机制,它是通过生成字节码的形式来实现的。如果反射调用的次数比较多,动态实现的效率就会更高,因为本地实现需要经过 Java 到 C/C++ 再到 Java 之间的切换过程,而动态实现不需要。但是如果反射调用的次数比较少,反而本地实现更快一些。

那临界点是多少呢?

默认是 15 此,可以通过 -Dsun.reflect.inflationThreshold 参数类调整。

Method setAgeMethod = clazz.getMethod("setAge", int.class);
for (int i = 0;i < 20; i++) {
    setAgeMethod.invoke(object, 18);
}

在 invoke() 方法处加断点进入 debug 模式,当 i = 15 的时候,也就是第 16 次执行的时候,会进入到 if 条件分支中,改变 DelegatingMethodAccessorImpl 的委派模式 delegate 为 (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(),而之前的委派模式 delegate 为 NativeMethodAccessorImpl。

在这里插入图片描述

04、总结

  1. 反射类及反射方法的获取,都是通过从列表中搜寻匹配的方法,所以查找性能会随类的大小方法多少而变化;
  2. 梅格雷都会有一个与之对应的 Class 实例,从而梅格雷都可以获取 method 反射方法,并作用到其他实例身上;
  3. 反射也是考虑了线程安全的,放心使用;
  4. 反射使用软引用 relectionData 缓存 class 信息,避免每次重新从 jvm 获取带来的开销;
  5. 反射调用多次生成新代理 Accessor,而通过字节码生存的则考虑了卸载的功能,所以会使用独立的类加载器;
  6. 当找到需要的方法,都会 copy 一份出来,而不是使用原来的实例,从而保证数据隔离;
  7. 调度反射方法,最终是由 jvm 执行 invoke() 方法。