反射机制
Table of Contents
通过 Java 的反射机制,程序员可以更深入地控制程序的运行过程。例如,在 程序运行时 由用户输入一个类名,然后动态获取该类拥有的构造、属性和方法,甚至调用任意类的任意方法。
反射机制是什么
Java 反射机制是 Java 语言的一个重要特性。在学习 Java 反射机制前,我们先来了解两个概念:编译期和运行期。
编译期 是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能, 并没有 把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。
运行期 是把编译后的文件交给计算机执行,直至程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法 ;对于任意一个对象,都能够调用它的任意属性和方法;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制 。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。
Java 反射机制在服务器和中间程序中得到了广泛运用。在服务端,往往需要根据客户的请求,动态调用某一个对象的特定方法。此外,在 ORM 中间件的实现中,运用 Java 反射机制可以读取任意一个 JavaBean 的所有属性,或者给这些属性赋值。

Java 反射机制主要提供了以下功能,这些功能都位于 java.lang.reflect
包:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理。
要想知道一个类的属性和方法,必须先获取该类的字节码文件对象。获取类的信息时,使用的就是 Class 类中的方法。所以,先要获取到每一个字节码文件( .class
)对应的 Class 类型的对象。
我们知道, 所有 Java 类均继承了 Object 类 ,在 Object 类中定义了一个 getClass()
方法,该方法返回同一个类型为 Class 的对象,如下:
1: Class labelCls = label1.getClass(); // label1 为 JLabel 类的对象
利用 Class 类的对象 labelCls
可以访问 labelCls
对象的描述信息、 JLabel
类的信息以及基类 Object 的信息。
下表列出了通过反射可以访问的信息:
类型 | 访问方法 | 返回值类型 | 说明 |
---|---|---|---|
包路径 | getPackage() |
Package 对象 | 获取该类的存放路径 |
类名称 | getName() |
String 对象 | 获取该类的名称 |
继承类 | getSuperclass() |
Class 对象 | 获取该类继承的类 |
实现接口 | getInterfaces() |
Class 型数组 | 获取该类实现的所有接口 |
构造方法 | getConstructors() |
Constructor 型数组 | 获取所有权限为 public 的构造方法 |
getDeclaredContructors() |
Constructor 对象 | 获取当前对象的所有构造方法 | |
方法 | getMethods() |
Methods 型数组 | 获取所有权限为 public 的方法 |
getDeclaredMethods() |
Methods 对象 | 获取当前对象的所有方法 | |
成员变量 | getFileds() |
Field 型数组 | 获取所有权限为 public 的成员变量 |
getDeclaredFields() |
Field 对象 | 获取当前对象的所有成员变量 | |
内部类 | getClasses() |
Class 型数组 | 获取所有权限为 public 的内部类 |
getDelaredClasses() |
Class 型数组 | 获取所有内部类 | |
内部类的声明类 | getDeclaringClass() |
Class 对象 | 如果该类为内部类,则返回它的成员类,否则返回 null |
*注:上表中,在调用 getFileds()
和 getMethods()
方法时将会依次获取权限为 public
的字段和变量,然后将包含从超类中继承到的成员变量和方法,而通过 getDeclaredFields()
和 getDeclaredMethods()
中只是获取在本类中定义的成员变量和方法。
Java 反射机制的优点:
- 能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性;
- 与 Java 动态编译相结合,可以实现无比强大的功能;
- 对于 Java 这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
Java 反射机制的缺点:
- 反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
- 反射调用方法时可以忽略权限检查,获取这个类的私有方法和属性,因此可能会破坏类的封装性而导致安全问题。
*注:Java 反射机制在一般的 Java 应用开发中很少使用,即便是 Java EE 阶段也很少使用。
反射机制 API
实现 Java 反射机制的类都位于 java.lang.reflect
包中, java.lang.Class
类是 Java 反射机制 API 中的核心类。
1. java.lang.Class 类
java.lang.Class
类是实现反射的关键所在,Class 类的一个实例表示 Java 的一种数据类型,包括类、接口、枚举、注解(Annotation)、数组、基本数据类型和 void 。Class 没有公有的构造方法,Class 实例是由 JVM 在类加载时自动创建的。
在程序代码中获得 Class 实例可以通过如下代码实现:
1: // 1. 通过类型 class 静态变量 2: Class clz1 = String.class; 3: // 2. 通过对象的 getClass() 方法 4: String str = "Hello"; 5: Class clz2 = str.getClass();
每一种类型包括类和接口等,都有一个 class
静态变量可以获得 Class 实例。另外,每一个对象都有 getClass()
方法获得 Class 实例,该方法是由 Object 类提供的实例方法。
Class 类提供了很多方法可以获得运行时对象的相关信息,下面的程序代码展示了其中一些方法。
1: public class ReflectionTest { 2: public static void main(String[] args) { 3: // 获得 Class 实例 4: // 1. 通过类型 class 静态变量 5: Class clz1 = String.class; 6: // 2. 通过对象的 getClass() 方法 7: String str = "Hello"; 8: Class clz2 = str.getClass(); 9: // 获得 int 类型的 Class 实例 10: Class clz3 = int.class; 11: // 获得 Integer 类型 Class 实例 12: Class clz4 = Integer.class; 13: 14: System.out.println("clz2 类名称:" + clz2.getName()); 15: System.out.println("clz2 是否为接口:" + clz2.isInterface()); 16: System.out.println("clz2 是否为数组对象:" + clz2.isArray()); 17: System.out.println("clz2 父类名称:" + clz2.getSuperclass().getName()); 18: System.out.println("clz2 是否为基本类型:" + clz2.isPrimitive()); 19: System.out.println("clz3 是否为基本类型:" + clz3.isPrimitive()); 20: System.out.println("clz4 是否为基本类型:" + clz4.isPrimitive()); 21: 22: // → clz2 类名称: java.lang.String 23: // → clz2 是否为接口: false 24: // → clz2 是否为数组对象: false 25: // → clz2 父类名称: java.lang.Object 26: // → clz2 是否为基本类型:false 27: // → clz3 是否为基本类型:true 28: // → clz4 是否为基本类型:false 29: } 30: } 31:
2. java.lang.reflect 包
java.lang.reflect
包提供了反射中用到的类,主要的类说明如下:
- Constructor 类:提供类的构造方法信息;
- Field 类:提供类或接口中成员变量信息;
- Mehtod 类:提供类或接口成员方法信息;
- Array 类:提供了动态创建和访问 Java 数组的方法;
- Modifer 类:提供类和成员访问修饰符信息。
示例代码如下:
1: public class ReflectionTest { 2: public static void main(String[] args) { 3: try { 4: // 动态加载 xx 类的运行时对象 5: Class c = Class.forName("java.lang.String"); 6: // 获取成员方法集合 7: Method[] methods = c.getDeclaredMethods(); 8: // 遍历成员方法集合 9: for (Method method : methods) { 10: // 打印权限修饰符,如 public、protected、private 11: System.out.println(Modifier.toString(method.getModifiers())); 12: // 打印返回值类型名称 13: System.out.println(method.getReturnType().getName()); 14: // 打印方法名称 15: System.out.println(method.getName() + "()"); 16: } 17: } catch (ClassNotFoundException e) { 18: System.out.println("找不到指定类"); 19: } 20: } 21: }
上述代码第 5 行是通过 Class 的静态方法 forName(String)
创建某个类的运行时对象,其中的参数是类全名字符串,如果在类路径中找不到这个类则抛出 ClassNotFoundException
异常,见代码第 17 行。