一、getName
方法解析
代码功能
public static String getName(String key) throws IOException {Properties properties = new Properties();FileInputStream in = new FileInputStream("D:\\路径...\\application.properties");properties.load(in); // 加载配置文件内容in.close(); // 关闭流return properties.getProperty(key); // 根据 key 返回对应的值 }
作用:
从 application.properties
配置文件中读取指定 key
的值(如 className
或 methodName
)。
配置文件示例
假设 application.properties
内容如下:
# 定义要反射的类名和方法名 className=com.example.TestInvoke methodName=printMessage
执行过程
-
加载文件:通过
FileInputStream
读取指定路径的配置文件。 -
解析配置:
Properties.load()
方法将文件内容解析为键值对。 -
获取值:
properties.getProperty("className")
返回"com.example.TestInvoke"
。
代码问题
-
硬编码路径:
D:\\路径...
是绝对路径,实际项目应使用相对路径或类路径加载。 -
资源未安全关闭:若
load()
抛出异常,in.close()
可能不会执行,建议用try-with-resources
。 -
异常处理简单:直接抛出
IOException
,未处理文件不存在等具体问题。
改进版本
public static String getName(String key) {// 使用类路径加载(文件需放在 resources 目录下)try (InputStream in = YourClass.class.getClassLoader().getResourceAsStream("application.properties")) {Properties properties = new Properties();properties.load(in);return properties.getProperty(key);} catch (IOException e) {throw new RuntimeException("加载配置文件失败", e);} }
二、反射机制详解
什么是反射?
反射(Reflection)是 Java 在 运行时 动态获取类信息并操作类的能力,包括:
-
动态加载类
-
创建对象
-
调用方法
-
访问私有成员
为什么需要反射?
-
解耦:代码不直接依赖具体类(如通过配置文件指定类名)。
-
灵活性:框架中自动装配对象(如 Spring 的
@Autowired
)。 -
通用工具:如 JSON 序列化、单元测试调用私有方法。
三、结合代码理解反射流程
完整流程
1. 读取配置文件 → 2. 获取类名和方法名 → 3. 反射加载类 → 4. 创建对象 → 5. 调用方法
分步解析
-
读取配置 通过
getName("className")
获取"com.example.TestInvoke"
。 -
加载类
Class<?> c = Class.forName("com.example.TestInvoke");
-
JVM 动态加载该类到内存(如果尚未加载)。
-
-
创建实例
TestInvoke obj = (TestInvoke) c.newInstance();
-
相当于执行
new TestInvoke()
(要求类有无参构造方法)。
-
-
获取并调用方法
Method method = c.getDeclaredMethod("printMessage"); method.invoke(obj);
-
即使
printMessage
是私有方法,setAccessible(true)
后仍可调用。
-
四、反射核心 API
操作 | API 示例 | 作用 |
---|---|---|
加载类 | Class.forName("全限定类名") | 获取类的 Class 对象 |
创建实例 | clazz.newInstance() 或 clazz.getConstructor().newInstance() | 实例化对象 |
获取方法 | clazz.getMethod("方法名", 参数类型...) | 获取公开方法 |
获取私有方法 | clazz.getDeclaredMethod("方法名") + setAccessible(true) | 访问私有方法 |
调用方法 | method.invoke(实例对象, 参数...) | 执行方法 |
五、示例:从配置到反射的完整过程
1. 配置文件 application.properties
className=com.example.UserService methodName=login
2. 目标类 UserService
package com.example; public class UserService {private void login() {System.out.println("用户登录成功!");} }
3. 反射调用代码
public static void main(String[] args) {try {// 从配置读取类名和方法名String className = getName("className"); // "com.example.UserService"String methodName = getName("methodName"); // "login" // 反射操作Class<?> clazz = Class.forName(className);Method method = clazz.getDeclaredMethod(methodName);method.setAccessible(true);Object instance = clazz.getDeclaredConstructor().newInstance();method.invoke(instance); // 输出:"用户登录成功!"} catch (Exception e) {e.printStackTrace();} }
六、反射的注意事项
-
性能开销:反射比直接调用慢,频繁调用需谨慎。
-
安全限制:模块化系统中(Java 9+)可能禁止访问私有成员。
-
破坏封装:反射可以绕过访问修饰符,滥用会导致代码难以维护。
-
类型安全:编译时无法检测类型错误,需自行保证类型正确性。
七、常见应用场景
-
框架开发:如 Spring 的依赖注入、MyBatis 的 Mapper 动态代理。
-
动态加载插件:通过配置文件指定实现类。
-
单元测试:测试私有方法(不推荐,但有时必要)。
-
通用工具:如通过反射实现对象拷贝、序列化工具。
总结
-
通过 配置文件 解耦了类名和方法名的硬编码,利用 反射 实现了动态调用。
-
反射是 Java 强大的特性,但需谨慎使用。理解其原理后,可以更深入掌握框架设计思想(如 Spring 的核心机制)。