Java应用运行时,如何动态加载类信息?实例化类?你可以使用Java反射机制
反射机制
官方定义为: Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine.
实际使用中,反射是,在Java虚拟机上处于运行时的应用中,能够动态加载类,实例化类,以及访问、修改类的属性和行为。
补充
- Java应用会经过编译时和运行时。运行时,由加载过程和执行主函数步骤组成。
- Java加载过程包括加载、链接、初始化和绑定native实现方法等步骤。
- Class.forName() 是将类加载进入内存,此时,虚拟机中并未存在该类的实例,而需要通过构造函数类Constructor的newInstance() 方法进行实例化。
应用场景
1. 拓展特性
应用程序可以使用用户定义的外部类,通过使用外部类的完全限定名称,创建可扩展性对象的实例。符合大多数的使用情况,例如Spring MVC用了反射。
示例
- Spring框架中动态代理使用反射创建代理类,例如工具类 org.springframework.cglib.core.ReflectUtils将jdk的反射功能封装起来。
- Spring框架中org.springframework.util.ClassUtils将java反射功能封装为工具类,提供Spring IoC和Spring AOP机制加载类和创建类实例的方法。
2. 类浏览器和可视化环境
类浏览器需要能够枚举出类的成员。可视化开发环境可受益于,利用反射中可用的类型信息帮助开发者写出正确的代码
示例
- Gradle中org.gradle.internal.impldep.bsh.util.ClassBrowser 通过反射获取所有的类信息。
3. 调试器和测试工具
调试器需要能够检测类的私有成员。测试工具可以利用反射系统地调用一个类中定义的暴露的APIs集,以确保测试集的高覆盖率。
示例
- Java测试框架JUnit中org.junit.internal.Classes封装java反射,ReflectiveRuntimeMXBean通过封装的工具类使用java反射获取MXBean。
代码演示
下面提供一个演示类ReflectDemo和类Person,以及main方法演示使用Class反射相关方法。这篇文章都基于这一小节的代码,
/**
* 演示Java的Reflect反射功能
* */
public class ReflectDemo {
// 地区枚举类
enum Region {
ASIA, AFRICA, AMERICA, EUROPE
}
/*
* 演示反射功能
* */
public static void main(String[] args){
try {
// 模拟获取指定类的全限定名,仅供演示使用,实际工作中根据情况获取
String classFullQualifiedPath = getDemoClassPath();
//1.类反射
Class clazz = Class.forName(classFullQualifiedPath);
//通过获取指定形参类型顺序数组获取构造函数,调用newInstance传入相应的实参列表进行实例化
Object newInstance = clazz.getConstructor(String.class, String.class, Region.class).newInstance("OneKnows", "178cm", Region.ASIA);
if (newInstance instanceof Person person)
out.format("New instance: %s%nname: %s%n%n", person, person.name);
else
out.format("New instance: %s%nname: %s%n", newInstance);
// 2. 成员反射
// 2.1 构造器反射
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
printInfos(declaredConstructors);
tryConstructorFeatures(declaredConstructors);
// 2.2 方法反射
Method[] declaredMethods = clazz.getDeclaredMethods();
printInfos(declaredMethods);
tryMethodFeatures(declaredMethods,newInstance);
// 2.3 变量反射
Field[] declaredFields = clazz.getDeclaredFields();
printInfos(declaredFields);
tryFieldFeatures(declaredFields, newInstance);
//3. 枚举反射
String enumClassFullQualifiedPath = getEnumDemoClassPath(); // 模拟获取指定枚举类的全限定名,仅供演示使用,实际工作中根据情况获取
Class<?> enumClazz = Class.forName(enumClassFullQualifiedPath);
//判断是否为枚举类
if (enumClazz.isEnum()){
Object[] enumConstants = enumClazz.getEnumConstants(); // 获取枚举常量数组
printInfos(enumConstants);
}
} catch (Exception e) {
e.printStackTrace(out);
}
}
public static void tryConstructorFeatures(Constructor[] declaredConstructors){
//隐藏具体实现
}
public static void tryMethodFeatures(Method[] declaredMethods, Object newInstance){
//隐藏具体实现
}
public static void tryFieldFeatures(Field[] fields, Object newInstance){
//隐藏具体实现
}
public static void printInfos(Object[] objects){
if (objects==null)return; //若为null,则结束
for (Object object : objects) {
out.format("Name: %s,%nValue: %s%n%n",object.getClass(), object);
}
}
// 给演示用,提供演示类的全限定路径名
public static String getDemoClassPath(){
return Person.class.getName();
}
public static String getEnumDemoClassPath(){
return Region