深入浅出 Java 反射机制,了解动态编程的原理,小白的速通指南
一、反射是什么Reflection反射是 Java 的一项强大特性它允许运行中的程序获取自身或任意类的内部信息如成员变量、方法、构造器并且可以动态创建对象、调用方法、修改字段甚至打破封装访问private成员。简单说把 Java 类的各个组成部分映射为对应的 Java 对象Class、Method、Field、Constructor等然后在运行时操控它们。经典应用场景1.开发框架Spring、MyBatis、Hibernate 都用反射实现依赖注入、ORM 映射。2.动态代理JDK 动态代理离不开反射。3.IDE 代码提示IDE 通过反射分析已加载的类结构。4.通用工具类比如通过反射实现任意对象的toString()、对象比对等二、为什么要使用反射反射的目的反射的核心在于实现“通用性”和“动态性”能力没有反射硬编码有了反射换一个同类型的类改代码重新编译改配置文件重启应用甚至热加载写一个处理任意类的工具每个类单独写一份写一次用Class对象去通用处理框架读取你的自定义类不可能框架是别人编译的框架通过反射动态加载你的类在最典型应用场景-框架中用户为框架提供的类名和内部细节都是未知的需要依赖反射来建立联系。框架预先不知道用户的自定义类但通过配置/注解/约定获得类名字符串然后利用反射在运行时动态加载、访问内部结构从而实现通用性。三、反射的入口一切从 Class 开始要操作一个类的结构必须先获得它的Class对象。有三种常见方式java// 1. 通过类名.class ClassPerson clazz1 Person.class; // 2. 通过对象.getClass() Person p new Person(); Class? clazz2 p.getClass(); // 3. 通过 Class.forName()最常用动态加载 Class? clazz3 Class.forName(com.example.Person);其中Class.forName()是反射的核心入口类名可以写在配置文件里实现真正的动态性。四、反射能做什么—— 动手写一个“类浏览器”简单实例下面我们设计一个ClassInspector工具它可以打印任意类的构造器、方法和字段信息并且动态调用其中的方法。1. 准备一个被反射的示例类javapublic class Calculator { private int result; public Calculator() { result 0; } private void add(int a) { result a; } public int getResult() { return result; } public void clear() { result 0; } }2. 使用反射获取类信息javaimport java.lang.reflect.*; public class ClassInspector { public static void inspect(String className) throws ClassNotFoundException { Class? clazz Class.forName(className); System.out.println( 类名 ); System.out.println(clazz.getName()); System.out.println(\n 构造器 ); for (Constructor? c : clazz.getDeclaredConstructors()) { System.out.println(c); } System.out.println(\n 方法 ); for (Method m : clazz.getDeclaredMethods()) { System.out.println(m); } System.out.println(\n 字段 ); for (Field f : clazz.getDeclaredFields()) { System.out.println(f); } } public static void main(String[] args) throws Exception { inspect(Calculator); } }运行后你会看到即使是private的方法和字段也被“暴露”了出来。3. 动态调用私有方法打破封装javapublic class ReflectionDemo { public static void main(String[] args) throws Exception { Class? clazz Class.forName(Calculator); Object obj clazz.getDeclaredConstructor().newInstance(); // 创建实例 // 获取私有方法 add(int) Method addMethod clazz.getDeclaredMethod(add, int.class); addMethod.setAccessible(true); // 压制 Java 访问检查 addMethod.invoke(obj, 10); // 调用公有方法 getResult Method getResult clazz.getMethod(getResult); Object result getResult.invoke(obj); System.out.println(计算结果: result); // 输出 10 } }这里的关键人物getDeclaredMethod()获取任意方法包括私有setAccessible(true)开启访问权限暴力反射invoke()执行方法五、反射的性能与注意事项优点动态性编译时不确定的类运行时才加载、调用。通用性可以写出与具体类无关的框架代码。缺点性能开销反射调用比直接调用慢 1~2 个数量级但现代 JVM 已有优化非极端场景可接受。安全隐患可以绕过访问控制破坏封装原则。代码可读性下降不如直接调用清晰。使用建议优先用接口和正常调用无法解耦时再考虑反射。高频调用的反射方法可以考虑缓存Method对象。不要滥用setAccessible(true)尊重设计者的封装意图。