前言

本章主要介绍 Java 反射的定义,反射相关类,以及反射 API 的使用。

推荐资料:韩顺平 Java 反射专题

版本约定

Java 反射机制允许程序在执行期间借助反射 API 获取任何类的内部信息(比如成员变量,构造器,成员方法等),并能操作对象的属性和方法。反射在设计模式和框架底层都会用到。

在加载完某个类之后,在堆中就会产生一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象包含了某个类的完整结构信息。通过这个对象就可以得到类的结构。这个对象就像一面镜子,透过这个镜子就可以看到类的结构,所以,形象的称之为反射。

关于反射的用途和缺点,参考官方文档:https://docs.oracle.com/javase/tutorial/reflect/index.html

反射机制原理图

Java 反射机制 - 图1
Java 程序在计算机中有三个阶段,分别是编译阶段、加载阶段、运行阶段,通常我们更加熟悉运行阶段。

比如我们编写了一个 Cat.java 类,它的内容如下所示:

  1. public class Cat {
  2. private String name;
  3. public Cat() {}
  4. public void hi() {}
  5. }

通过 javac 命令完成 Cat 类的编译,生成一个 Cat.class 的字节码文件。

当我们运行Cat cat = new Cat()代码的时候,类加载器(ClassLoader)会将 Cat 类的字节码文件加载到堆内存中,生成一个 Class 类对象,这个 Class 类对象包含了 Cat 类的完整结构信息(成员变量、构造器、成员方法等等)。类加载器的这一动作就提现了 Java 的反射机制。

当 Cat 类加载完后,就会生成一个 Cat 对象,这个对象也存在在堆内存中,并且该对象知道它是属于那个 Class 类对象的,可以简单理解成 Cat 对象和 Class 类对象之间存在映射关系,所以,我们通过cat.getClass()方法可以得到它的 Class 类对象。

上图可以分为两部分,左边部分 JVM 完成类的加载工作(字节码编译不属于 JVM 范畴),右边部分属于应用,我们在拿到某个类的 Class 类对象之后,可以创建这个类的对象,调用这个类的方法,操作这个类的属性等。

反射主要类

反射相关的主要类有如下几个:

  • java.lang.Class:代表一个类;
  • java.lang.reflect.Method:代表类的成员方法;
  • java.lang.reflect.Field:代表类的成员变量;
  • java.lang.reflect.Constructor:代表类的构造方法。

    Class 类

Class 类是我们使用反射功能的入口,一切反射的使用都要从获得一个 Class 类对象开始。

引用一段《Java 核心技术 卷1 基础知识》中描述 Class 类的内容:

在程序运行期间,Java 运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。

可以通过专门的 Java 类访问这些信息,保存这些信息的类被称为 Class。

Class 类具有如下几种特性:

  1. Class 类和其他类是一样的,都继承了 Object 类,只是这个类的功能有一些特殊,它的类图如下所示。

image.png

  1. Class 类对象不是 new 出来的,而是 JVM 创建的。通过上面的反射机制原理图,可以知道 Class 类对象是通过类加载器(ClassLoader)生成的。

image.png
Debug 上面的代码,Force Step Into,可以进入到 ClassLoader.loadClass 方法中。
image.png

  1. 对于某个类的 Class 类对象,在内存中只有一份,因为类只加载一次。关于这点,也好解释,上面的代码,如果我们继续对反射方式 Debug 的时候,就不会再次调用 ClassLoader.loadCalss 方法了,因为传统方式已经加载了 Cat 类。另外,通过下面的代码也可以看出来。

    1. public static void main(String[] args) throws ClassNotFoundException {
    2. Class<?> cat1 = Class.forName("test15.Cat");
    3. Class<?> cat2 = Class.forName("test15.Cat");
    4. System.out.println("cat1.hashCode(): " + cat1.hashCode());
    5. System.out.println("cat2.hashCode(): " + cat2.hashCode());
    6. }

    运行程序,输出:

    1. cat1.hashCode(): 403424356
    2. cat2.hashCode(): 403424356

    两个对象的 hashCode 一样。

  2. 每个类的对象都知道自己是由哪个 Class 类对象生成的。

    1. public static void main(String[] args) {
    2. Cat cat = new Cat();
    3. Class clz = cat.getClass();
    4. }

    比如可以通过 Cat 类对象获取它对应的 Class 类对象。

  3. 通过 Class 类对象可以得到一个类的完整结构。

  4. Class 类对象是存放在堆中的。
  5. 类的字节码二进制数据,存放在方法区。

获取 Class 类对象的几种方式:

  1. 已知某个类的全类名,且该类在类路径下,可通过 Class 类的静态方法 forName() 获取 Class 类对象,可能抛出 ClassNotFoundException;
    1. Class cls1 = Class.forName("test1.Cat");
  2. 已知具体的类,通过类的 class 获取 Class 类对象,该方式最为安全可靠,且程序性能最高;
    1. Class cls2 = Cat.class;
  3. 已知某个类的实例,调用该实例的 getClass() 方法获取 Class 类对象;
    1. Class cls3 = 对象.getClass();
  4. 通过类加载器获取 Class 类对象;
    1. ClassLoader cl = 对象.getClass().getClassLoader();
    2. Class cls4 = cl.loadClass("类的全类名");
  5. 基本数据类型(int,char,boolean,float,double,byte,long,short)和数组类型按如下方式得到 Class 类对象;
    1. Class cls5_1 = 基本数据类型.class;
    2. Class cls5_2 = 数组类型.class;
  6. 基本数据类型对应的包装类,可通过 TYPE 得到 Class 类对象;
    1. Class cls6 = 包装类.TYPE;
      1. public static void main(String[] args) throws ClassNotFoundException {
      2. Class cls1 = Class.forName("test1.Cat");
      3. Class cls2 = Cat.class;
      4. Class cls3 = new Cat().getClass();
      5. Class cls4 = Test9.class.getClassLoader().loadClass("test1.Cat");
      6. Class cls5_1 = int.class;
      7. Class cls5_2 = int[].class;
      8. Class cls6 = Integer.TYPE;
      9. }
      常用的就前面三种方式。

Class 类的方法比较多,常用的方法就这么几种:

  • 获取 Class 类对象:forName
  • 获得实体的名称:getName
  • 动态地创建一个类的实例:newInstance
  • 获得实体的成员变量(Field)相关的方法:getFields,getField,getDeclaredFields,getDeclaredField
  • 获得实体的成员方法(Method)相关的方法:getMethods,getMethod,getDeclaredMethods,getDeclaredMethod
  • 获得实体的构造函数(Constructor)相关的方法:getConstructors,getDeclaredConstructors,getConstructor,getDeclaredConstructor

以上方法的具体使用以及其他方法的介绍参考:Class 类

其他类

反射的其他主要类 Field,Method 以及 Constructor 都是通过 Class 类获取的,我们在案例中介绍。

案例

利用反射分析类的能力

在 java.lang.reflect 包中有三个类 Field、Method 和 Constructor 分别用于描述类的域、方法和构造器。

Class 类中的 getFields、getMethods 和 getConstructors 方法将分别返回类提供的 public 域、方法和构造器数组,其中包括超类的共有成员。

Class 类的 getDeclareFields、getDeclareMethods 和 getDeclaredConstructors 方法将分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护成员,但不包括超类的成员。

Field、Method 和 Constructor 都提供了哪些常见的方法用来分析类的能力?

  • Field、 Method 和 Constructor 都有一个叫做 getName 的方法, 用来返回项目的名称;
  • Field 类有一个 getType 方法, 用来返回描述域所属类型的 Class 对象;
  • Method 和 Constructor 类有能够获取方法参数类型的方法,Method 类还有一个可以获取方法返回类型的方法;
  • 这三个类还有一个叫做 getModifiers 的方法,它将返回一个整型数值,用不同的位开关描述 public 和 static 这样的修饰符使用状况。
  • 可以利用 java.lang.reflect 包中的 Modifier 类的静态方法分析 getModifiers 方法返回的整型数值。例如, 可以使用 Modifier 类中的 isPublic、 isPrivate 或 isFinal 判断方法或构造器是否是 public、 private 或 final。

接下来,我们利用反射,打印出一个类的全部信息(域、方法和构造器)。

  1. public class ReflectionTest {
  2. public static void main(String[] args) throws ClassNotFoundException {
  3. String name = "java.lang.Double";
  4. Class clazz = Class.forName(name);
  5. Class superClazz = clazz.getSuperclass();
  6. String modifiers = Modifier.toString(clazz.getModifiers());
  7. if (modifiers.length() > 0) {
  8. System.out.print(modifiers + " ");
  9. }
  10. System.out.print("class " + name);
  11. if (superClazz != null && superClazz != Object.class) {
  12. System.out.print(" extends " + superClazz.getName());
  13. }
  14. System.out.print("\n{\n");
  15. printConstructors(clazz);
  16. System.out.println();
  17. printMethods(clazz);
  18. System.out.println();
  19. printFields(clazz);
  20. System.out.println("}");
  21. }
  22. /**
  23. * Prints all constructors of a class
  24. *
  25. * @param clazz
  26. */
  27. public static void printConstructors(Class clazz) {
  28. Constructor[] constructors = clazz.getDeclaredConstructors();
  29. for (Constructor constructor : constructors) {
  30. String name = constructor.getName();
  31. System.out.print(" ");
  32. String modifiers = Modifier.toString(constructor.getModifiers());
  33. if (modifiers.length() > 0) {
  34. System.out.print(modifiers + " ");
  35. }
  36. System.out.print(name + "(");
  37. Class[] paramTypes = constructor.getParameterTypes();
  38. for (int i = 0; i < paramTypes.length; i++) {
  39. if (i > 0) {
  40. System.out.print(", ");
  41. }
  42. System.out.print(paramTypes[i].getName());
  43. }
  44. System.out.println(");");
  45. }
  46. }
  47. /**
  48. * Prints all methods of a class
  49. *
  50. * @param clazz
  51. */
  52. public static void printMethods(Class clazz) {
  53. Method[] methods = clazz.getDeclaredMethods();
  54. for (Method method : methods) {
  55. Class returnType = method.getReturnType();
  56. String name = method.getName();
  57. System.out.print(" ");
  58. String modifiers = Modifier.toString(method.getModifiers());
  59. if (modifiers.length() > 0) {
  60. System.out.print(modifiers + " ");
  61. }
  62. System.out.print(returnType.getName() + " " + name + "(");
  63. Class[] paramTypes = method.getParameterTypes();
  64. for (int i = 0; i < paramTypes.length; i++) {
  65. if (i > 0) {
  66. System.out.print(", ");
  67. }
  68. System.out.print(paramTypes[i].getName());
  69. }
  70. System.out.println(");");
  71. }
  72. }
  73. /**
  74. * Prints all fields of a class
  75. *
  76. * @param clazz
  77. */
  78. public static void printFields(Class clazz) {
  79. Field[] fields = clazz.getDeclaredFields();
  80. for (Field field : fields) {
  81. Class type = field.getType();
  82. String name = field.getName();
  83. System.out.print(" ");
  84. String modifiers = Modifier.toString(type.getModifiers());
  85. if (modifiers.length() > 0) {
  86. System.out.print(modifiers + " ");
  87. }
  88. System.out.println(type.getName() + " " + name + ";");
  89. }
  90. }
  91. }

运行程序,输出:

  1. public final class java.lang.Double extends java.lang.Number
  2. {
  3. public java.lang.Double(double);
  4. public java.lang.Double(java.lang.String);
  5. public boolean equals(java.lang.Object);
  6. public static java.lang.String toString(double);
  7. public java.lang.String toString();
  8. public int hashCode();
  9. public static int hashCode(double);
  10. public static double min(double, double);
  11. public static double max(double, double);
  12. public static native long doubleToRawLongBits(double);
  13. public static long doubleToLongBits(double);
  14. public static native double longBitsToDouble(long);
  15. public volatile int compareTo(java.lang.Object);
  16. public int compareTo(java.lang.Double);
  17. public byte byteValue();
  18. public short shortValue();
  19. public int intValue();
  20. public long longValue();
  21. public float floatValue();
  22. public double doubleValue();
  23. public static java.lang.Double valueOf(java.lang.String);
  24. public static java.lang.Double valueOf(double);
  25. public static java.lang.String toHexString(double);
  26. public static int compare(double, double);
  27. public static boolean isNaN(double);
  28. public boolean isNaN();
  29. public static boolean isFinite(double);
  30. public static boolean isInfinite(double);
  31. public boolean isInfinite();
  32. public static double sum(double, double);
  33. public static double parseDouble(java.lang.String);
  34. public abstract final double POSITIVE_INFINITY;
  35. public abstract final double NEGATIVE_INFINITY;
  36. public abstract final double NaN;
  37. public abstract final double MAX_VALUE;
  38. public abstract final double MIN_NORMAL;
  39. public abstract final double MIN_VALUE;
  40. public abstract final int MAX_EXPONENT;
  41. public abstract final int MIN_EXPONENT;
  42. public abstract final int SIZE;
  43. public abstract final int BYTES;
  44. public final java.lang.Class TYPE;
  45. public abstract final double value;
  46. public abstract final long serialVersionUID;
  47. }
  48. Process finished with exit code 0

上面的代码打印出了 Double 类的结构。

利用反射分析对象

从前面一节中,我们已经知道如何利用 Field、Method 和 Constructor 中提供的方法来分析类的能力。本节将进一步利用这三个反射类来分析对象。

修改一下 Cat 类,下面继续使用该类演示反射功能。

  1. public class Cat {
  2. private String name = "小黑";
  3. public int age = 1;
  4. public Cat() {
  5. }
  6. private Cat(String name, int age) {
  7. this.name = name;
  8. this.age = age;
  9. }
  10. public String getName() {
  11. return this.name;
  12. }
  13. public int getAge() {
  14. return age;
  15. }
  16. private void setName(String name) {
  17. this.name = name;
  18. }
  19. public static void printCat(String name, int age) {
  20. System.out.println("猫的名字:" + name + ",猫的年龄:" + age);
  21. }
  22. @Override
  23. public String toString() {
  24. return "Cat{" +
  25. "name='" + name + '\'' +
  26. ", age=" + age +
  27. '}';
  28. }
  29. }

利用 Constructor 类中的方法动态的创建类的实例

  1. 首先,我们需要先获得某个 Constructor 对象;
  2. 通过这个 Constructor 对象创建指定类的实例。

    1. public static void main(String[] args) throws Exception {
    2. Class cls = Cat.class;
    3. // 1.获得无参Constructor对象
    4. Constructor constructor1 = cls.getConstructor();
    5. // 利用无参构造函数创建Cat类的实例
    6. Cat cat1 = (Cat) constructor1.newInstance(null);
    7. // 会调用cat的toString方法
    8. System.out.println(cat1);
    9. // 2.获得有参私有Constructor对象
    10. Constructor constructor2 = cls.getDeclaredConstructor(String.class, int.class);
    11. // 利用有参构造函数创建Cat类的实例
    12. Cat cat2 = (Cat) constructor2.newInstance("小白", 2);
    13. // 会调用cat的toString方法
    14. System.out.println(cat2);
    15. }

    运行程序,输出:

    1. Cat{name='小黑', age=1}
    2. Exception in thread "main" java.lang.IllegalAccessException: class test1.Test10 cannot access a member of class test1.Cat with modifiers "private"
    3. at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)
    4. at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:591)
    5. at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
    6. at test1.Test10.main(Test10.java:24)

    我们已经注意到在获取私有的构造函数的时候需要使用getDeclaredConstructor方法,但是上面的代码任然抛出 IllegalAccessException 异常,什么原因?

Java 程序受到安全管理器的控制,只允许查看任意对象有哪些构造函数,而不允许调用 Constructor 的 newInstance 方法创建类的实例。同样的,Field 的 get 方法和 Method 的 invoke 方法同样存在这样的问题。

为了让 Java 程序不受到安全管理器的控制,需要调用 Field、Method 或 Constructor 对象的 setAccessible 方法。setAccessible 方法是 AccessibleObject 类中的一个方法,它是 Field、Method 和 Constructor 类的公共超类。

比如上面的代码可以添加:constructor2.setAccessible(true);

  1. public static void main(String[] args) throws Exception {
  2. Class cls = Cat.class;
  3. // 1.获得无参Constructor对象
  4. Constructor constructor1 = cls.getConstructor();
  5. // 利用无参构造函数创建Cat类的实例
  6. Cat cat1 = (Cat) constructor1.newInstance(null);
  7. // 会调用cat的toString方法
  8. System.out.println(cat1);
  9. // 2.获得有参私有Constructor对象
  10. Constructor constructor2 = cls.getDeclaredConstructor(String.class, int.class);
  11. // 利用有参构造函数创建Cat类的实例
  12. constructor2.setAccessible(true);
  13. Cat cat2 = (Cat) constructor2.newInstance("小白", 2);
  14. // 会调用cat的toString方法
  15. System.out.println(cat2);
  16. }

运行程序,输出:

  1. Cat{name='小黑', age=1}
  2. Cat{name='小白', age=2}

这样就不会抛出异常了。

利用 Field 类中的方法操作域

  1. 首先,我们需要先获得某个域的 Field 对象;
  2. 通过这个 Field 对象操作指定的域。

    1. public static void main(String[] args) throws Exception {
    2. Cat cat = new Cat();
    3. Class cls = Cat.class;
    4. // 1.获得共有字段age的Field对象
    5. Field ageField = cls.getField("age");
    6. // age是int类型,可以使用Field类中的getInt方法
    7. int ageVal = ageField.getInt(cat);
    8. System.out.println("第1次 age:" + ageVal);
    9. // 使用setInt方法重设age的值
    10. ageField.setInt(cat, 2);
    11. ageVal = ageField.getInt(cat);
    12. System.out.println("第2次 age:" + ageVal);
    13. // 2.获得私有字段name的Field对象
    14. Field nameField = cls.getDeclaredField("name");
    15. // 同样私有方法需要设置setAccessible方法,获得访问权
    16. nameField.setAccessible(true);
    17. // name是String类型,可以使用Field类中的get方法
    18. String nameVal = (String) nameField.get(cat);
    19. System.out.println("第1次 name:" + nameVal);
    20. // 使用set方法重设name的值
    21. nameField.set(cat, "小白");
    22. nameVal = (String) nameField.get(cat);
    23. System.out.println("第2次 name:" + nameVal);
    24. }

    运行程序,输出:

    1. 1 age1
    2. 2 age2
    3. 1 name:小黑
    4. 2 name:小白

    同样的,name 是一个私有域,所以需要调用 setAccessible 方法,获得访问权。另外,如果域是 int 类型,可以使用 Field 类中的 getInt,setInt 方法操作该域,其他基本类型也有对应的方法。

利用 Method 类中的方法动态的执行实例的方法

  1. 首先,我们需要先获得某个方法的 Method 对象;
  2. 通过这个 Method 对象操作指定的方法。

    1. public static void main(String[] args) throws Exception {
    2. Cat cat = new Cat();
    3. Class cls = Cat.class;
    4. // 1.获得共有方法getName的Method对象
    5. Method method1 = cls.getMethod("getName");
    6. // 执行方法
    7. String name = (String) method1.invoke(cat);
    8. System.out.println("猫的名字:" + name);
    9. // 2.获得私有方法setName的Method对象
    10. Method method2 = cls.getDeclaredMethod("setName", String.class);
    11. // 执行方法
    12. method2.setAccessible(true);
    13. method2.invoke(cat, "小白");
    14. System.out.println(cat);
    15. // 3.获得静态方法printCat的Method对象
    16. Method method3 = cls.getMethod("printCat", String.class, int.class);
    17. // 执行静态方法,不需要传Cat对象
    18. method3.invoke(null, "小白", 2);
    19. }

    运行程序,输出:

    1. 猫的名字:小黑
    2. Cat{name='小白', age=1}
    3. 猫的名字:小白,猫的年龄:2

    如果返回类型是基本类型,invoke 方法会返回其包装器类型。比如,method1 表示 Cat 类的 getAge 方法,那么返回的对象实际上是一个 Integer,必须相应地完成类型转换,可以使用自动拆箱将它转换为一个 int:int age = (Integer) method1.invoke(cat);

如果存在多个相同名字的方法,可以通过方法的参数类型来区分。比如,还存在一个 getName 方法,需要传一个 String 类型的参数,则可以通过cls.getMethod("getName", String.class);来区分方法。

如果调用的方法是一个静态方法,invoke 方法的第一个参数传 null。

引用一段《Java 核心技术 卷1 基础知识》中描述 Method 类的内容:

上述程序清楚地表明,可以使用 method 对象实现 C 语言中函数指针的所有操作。同 C 一样,这种程序设计风格并不太简便,出错的可能性也比较大。如果在调用方法的时候提供了一个错误的参数,那么 invoke 方法将会抛出一个异常。

另外,invoke 的参数和返回值必须是 Object 类型的。这就意味着必须进行多次的类型转换。这样做将会使编译器错过检查代码的机会。因此,等到测试阶段才会发现这些错误,找到并改正它们将会更加困难。不仅如此,使用反射获得方法指针的代码要比仅仅直接调用方法明显慢一些。

有鉴于此,建议仅在必要的时候才使用 Method 对象,而最好使用接口以及 Java SE 8 中的 lambda 表达式。特别要重申:建议 Java 开发者不要使用 Method 对象的回调功能。使用接口进行回调会使得代码的执行速度更快,更易于维护。

反射的优点和缺点

  • 优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
  • 缺点:使用反射基本是解释执行,对执行速度有影响。

我们用一个例子来比较用传统方式调用方法和反射方式调用方法,两者的执行速度是不是差别很大。

  1. public class Test7 {
  2. public static void main(String[] args) throws Exception {
  3. m1();
  4. m2();
  5. }
  6. public static void m1() {
  7. Cat cat = new Cat();
  8. long startTime = System.currentTimeMillis();
  9. for (int i = 0; i < 100000000; i++) {
  10. cat.hi();
  11. }
  12. long endTime = System.currentTimeMillis();
  13. System.out.println("传统方式调用 hi 方法执行时间:" + (endTime - startTime));
  14. }
  15. public static void m2() throws Exception {
  16. Class cls = Class.forName("test15.Cat");
  17. Object o = cls.newInstance();
  18. Method hi = cls.getMethod("hi");
  19. long startTime = System.currentTimeMillis();
  20. for (int i = 0; i < 100000000; i++) {
  21. hi.invoke(o);
  22. }
  23. long endTime = System.currentTimeMillis();
  24. System.out.println("反射方式调用 hi 方法执行时间:" + (endTime - startTime));
  25. }
  26. }

运行程序,输出:

  1. 传统方式调用 hi 方法执行时间:3
  2. 反射方式调用 hi 方法执行时间:270

对比两者的执行时间,确实差别比较大。有没有办法优化反射的性能?

反射优化的空间有限,可以通过关闭访问检查来优化一些反射的性能。

什么是访问检查?

Method、Field 和 Constructor 对象都有 setAccessible() 方法,setAccessible() 的作用是启用和禁用访问安全检查,当传入方法的参数值为 true 时,表示反射的对象在使用时取消访问检查,提高反射的效率;当传入方法的参数值为 false 时,表示反射的对象在使用时需要执行访问检查。

打开 Field 的类图,它继承了 AccessibleObject 类,在 AccessibleObject 类中有 setAccessible 方法。
image.png
修改上面的代码,在调用 hi() 方法之前,先执行 hi.setAccessible(true); 取消访问检查。

  1. public class Test7 {
  2. public static void main(String[] args) throws Exception {
  3. m1();
  4. m2();
  5. }
  6. public static void m1() {
  7. Cat cat = new Cat();
  8. long startTime = System.currentTimeMillis();
  9. for (int i = 0; i < 100000000; i++) {
  10. cat.hi();
  11. }
  12. long endTime = System.currentTimeMillis();
  13. System.out.println("传统方式调用 hi 方法执行时间:" + (endTime - startTime));
  14. }
  15. public static void m2() throws Exception {
  16. Class cls = Class.forName("test15.Cat");
  17. Object o = cls.newInstance();
  18. Method hi = cls.getMethod("hi");
  19. hi.setAccessible(true);
  20. long startTime = System.currentTimeMillis();
  21. for (int i = 0; i < 100000000; i++) {
  22. hi.invoke(o);
  23. }
  24. long endTime = System.currentTimeMillis();
  25. System.out.println("反射方式调用 hi 方法执行时间:" + (endTime - startTime));
  26. }
  27. }

运行程序,输出:

  1. 传统方式调用 hi 方法执行时间:3
  2. 反射方式调用 hi 方法执行时间:124

可以看到,反射的执行时间确实少了一些。

参考

  1. Java 核心技术
  2. Carson带你学Java:这是一份全面&详细的Java反射学习指南
  3. 详解面试中常考的 Java 反射机制
  4. Java基础与提高干货系列——Java反射机制

作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/lkv999 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。