反射机制的理解

一、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 的值(如 classNamemethodName)。

配置文件示例

假设 application.properties 内容如下:

复制代码
# 定义要反射的类名和方法名
className=com.example.TestInvoke
methodName=printMessage
执行过程
  1. 加载文件 :通过 FileInputStream 读取指定路径的配置文件。

  2. 解析配置Properties.load() 方法将文件内容解析为键值对。

  3. 获取值properties.getProperty("className") 返回 "com.example.TestInvoke"

代码问题
  1. 硬编码路径D:\\路径... 是绝对路径,实际项目应使用相对路径或类路径加载。

  2. 资源未安全关闭 :若 load() 抛出异常,in.close() 可能不会执行,建议用 try-with-resources

  3. 异常处理简单 :直接抛出 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 在 运行时 动态获取类信息并操作类的能力,包括:

  • 动态加载类

  • 创建对象

  • 调用方法

  • 访问私有成员

为什么需要反射?
  1. 解耦:代码不直接依赖具体类(如通过配置文件指定类名)。

  2. 灵活性 :框架中自动装配对象(如 Spring 的 @Autowired)。

  3. 通用工具:如 JSON 序列化、单元测试调用私有方法。


三、结合代码理解反射流程
完整流程
复制代码
1. 读取配置文件 → 2. 获取类名和方法名 → 3. 反射加载类 → 4. 创建对象 → 5. 调用方法
分步解析
  1. 读取配置 通过 getName("className") 获取 "com.example.TestInvoke"

  2. 加载类

    复制代码
    Class<?> c = Class.forName("com.example.TestInvoke");
    • JVM 动态加载该类到内存(如果尚未加载)。
  3. 创建实例

    复制代码
    TestInvoke obj = (TestInvoke) c.newInstance();
    • 相当于执行 new TestInvoke()(要求类有无参构造方法)。
  4. 获取并调用方法

    复制代码
    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();
    }
}

六、反射的注意事项
  1. 性能开销:反射比直接调用慢,频繁调用需谨慎。

  2. 安全限制:模块化系统中(Java 9+)可能禁止访问私有成员。

  3. 破坏封装:反射可以绕过访问修饰符,滥用会导致代码难以维护。

  4. 类型安全:编译时无法检测类型错误,需自行保证类型正确性。


七、常见应用场景
  1. 框架开发:如 Spring 的依赖注入、MyBatis 的 Mapper 动态代理。

  2. 动态加载插件:通过配置文件指定实现类。

  3. 单元测试:测试私有方法(不推荐,但有时必要)。

  4. 通用工具:如通过反射实现对象拷贝、序列化工具。


总结
  • 通过 配置文件 解耦了类名和方法名的硬编码,利用 反射 实现了动态调用。

  • 反射是 Java 强大的特性,但需谨慎使用。理解其原理后,可以更深入掌握框架设计思想(如 Spring 的核心机制)。

相关推荐
捡田螺的小男孩16 分钟前
CompletableFuture使用的6个坑
java·后端
_半夏曲16 分钟前
为什么List、Set集合无法在遍历的时候修改内部元素
java·数据结构·list
算法与编程之美34 分钟前
利用java实现数据分析
java·开发语言·python·数据挖掘·数据分析
阿湯哥40 分钟前
IDEA Reformat Code 避免将多行参数或多行方法链调用合并成一行
java·ide·intellij-idea
SZ17011023142 分钟前
eclipse运行问题
java·ide·eclipse
龙鹤鹿1 小时前
langchain4j官方文档解读
java·人工智能
&白帝&1 小时前
MyBatis一对多查询方式
java·tomcat·mybatis
梦想家星空1 小时前
优化 Java 数据结构选择与使用,提升程序性能与可维护性
java·开发语言·数据结构
桑桑程序员1 小时前
Java Stream API 之 flatMap
java