1.1 Java反射概述
反射(Reflection)机制是Java语言特性之一,是Java被视为动态(或准动态)语言的一个关键特性。
1.1.1 什么是反射
在计算机领域,反射指一种能力,能够自描述和自控制,即在运行状态中,动态获取类信息及动态调用实例方法的能力。
Java反射有以下3个动态特性。
▶ 运行时创建实例。
▶ 运行期间调用方法。
▶ 运行时更改属性。
如何理解Java的反射机制呢?首先来回顾一下Java程序的执行过程。Java程序要想运行,Java类必须被Java虚拟机加载。
之前我们所运行的程序都是在编译时就已经链接了所有所需的类,而Java反射机制则允许程序在运行时再加载、探知、使用那些在编译时完全未知的类。例如,在之前课程中使用JDBC时,使用Class.forName()方法通过一个字符串形式的类名查找并加载驱动类,就是对反射机制的初步运用。每个项目中使用的数据库产品和版本都不是一成不变的,在代码中用new关键字实例化驱动类的传统做法是不明智的,而利用反射机制则可以在运行时读取配置文件中的驱动类名,动态加载所需的驱动类。
反射机制允许Java程序加载一个运行时才得知其名称的类,获悉其完整API信息,包括其修饰符(诸如public、static等)、超类、实现的接口,也包括属性和方法的所有信息;并可生成其实例、对其属性赋值或调用其方法。通过Java反射可以实现以下功能。
▶ 在运行时探知任意一个实例所属的类。
▶ 在运行时构造任意一个类的实例。
▶ 在运行时探知任意一个类所具有的方法和属性。
▶ 在运行时调用任意一个实例的方法。
就像照镜子能够看清自己,反射使程序可以看清一个类的情况并加以使用。Java反射机制能够探知类的基本结构,这种对Java类结构探知的能力,称为Java类的“自审”。并且,反射机制是构建框架技术的基础所在,掌握Java反射机制,对以后学习框架技术有很大的帮助。
1.1.2 Java反射常用API
使用Java反射技术,常用的类如下。
▶ java.lang.Class<T>类:反射的核心类,反射所有的操作都是围绕该类来生成的。通过Class类可以获取类的属性、方法等内容信息。
▶ java.lang.reflect.Constructor<T>类:表示类的构造方法。
▶ java.lang.reflect.Field类:表示类的属性,可以获取和设置类中属性的值。
▶ java.lang.reflect.Method类:表示类的方法,可以用来获取类中方法的信息或执行方法。
1.2 反射的的应用
在Java程序中使用反射的基本步骤如下。
(1)导入java.lang.reflect包中的相关类。
(2)获得需要操作的类的Class实例。
(3)调用Class实例的方法获取Field、Method等实例。
(4)使用反射API操作实例成员。
1.2.1 获取类的信息
1. 获取Class实例
▶ 调用类或接口实例的getClass()方法。
Class clz = obj.getClass(); // obj为某个类型的实例
▶ 调用类或接口的class属性。
Class clz = Student.class; // Student 为自定义的学生类型
▶ 使用Class.forName()方法。
Class clz = Class.forName("com.mysql.cj.jdbc.Driver");
2. 从Class实例获取信息
在获得了某个类型对应的Class实例之后,就可以调用Class实例的方法来获得该类型的信息。Class类提供了大量实例方法来获取对应类型的详细信息。
▶ 获取对应类型的基本信息,相关方法:获取对应类型的基本信息的方法:方法名:String getName(),说明:以字符串形式返回该类型的名称。方法名:String getSimpleName(),说明:以字符串形式返回该类型的简称。方法名:Package getPackage(),说明:获取该类型所在的包。方法名:Class getSuperclass(),说明:返回该类型的超类的Class实例。方法名:Class[] getInterfaces(),说明:返回该类型所实现的全部接口的Class实例。方法名:int getModifiers(),说明:返回该类型的所有修饰符,由public、protected、private、final、static、abstract等对应的int常量组成,返回的整数应使用Modifier工具类来解码,才可以判断修饰符的构成。方法名:Class[] getDeclaredClasses(),说明:返回该类型中包含的全部内部类的Class实例,方法名:Class getDeclaringClass(),说明:返回该类型所在的外部类的Class实例。
1.2.2 创建实例
通过反射来创建Java类型的实例有如下两种方式。
▶ 使用Class实例的newInstance()方法创建相关类型的实例。
▶ 使用Constructor实例创建相关类型的实例。
1.2.3 访问类的属性
使用Field实例可以对属性进行取值或赋值操作,主要方法:访问属性的方法:方法:xxx getXxx(Object obj),说明:xxx表示8种基本数据类型之一,如int getInt(Object obj)。obj为该属性所在类的实例。假设instance表示A类的实例,field表示A类中的属性a,则field.getInt(instance)表示以int类型返回instance中属性a的值。若Field实例表示的是一个静态属性,则obj可以设置为null。方法:Object get(Object obj),说明:以Object类型返回obj中相关属性的值。方法:void setXxx(Object obj, xxx val),说明:将obj中相关属性的值设置为val。xxx为8种基本数据类型之一。方法:void set(Object obj,Object val),说明:将obj中相关属性的值设置为val。方法:void setAccessible(boolean flag),说明:对相关属性设置访问权限。设置为true可以禁止Java语言访问检查。
1.2.4 调用类的方法
Method类中包含一个invoke()方法,通过invoke()方法,Method实例可以调用Java类的实例方法和静态方法。invoke()方法定义如下:
Object invoke(Object obj, Object... args);
其中,obj是执行该方法的对象,args是执行该方法时传入的参数。
例如,method表示A类中的fun()方法,instance是A类的实例,则method.invoke(instance,args)表示调用instance的fun()方法并传入参数args。
若Method实例表示的是一个静态方法,则obj可以为null。
本章总结
◎ Java反射机制是指在运行状态中,动态获取类型信息及动态访问实例成员的能力。
◎ 使用反射可以在程序运行时创建类的实例及访问其属性和方法。
◎ 反射在Java框架技术中有着大量的应用。