Java反射机制——运行时“透视“类的秘密 一个让我困惑的问题学Java面向对象时老师常说先定义类再创建对象然后调用方法。 这很合理。但后来我接触到一些框架比如Spring、MyBatis发现它们有个邪门的能力在运行时它们能创建一个类的对象、调用它的方法甚至修改私有字段的值——而这一切事先根本不知道这个类是什么这是怎么做到的答案就是 Java反射机制Reflection。二、反射是什么一句话概括在运行时动态地获取类的信息并操作类或对象的能力。正常写代码时类的结构在编译期就确定了Student s new Student(); // 编译时就知道有 Student 类 s.study(); // 编译时就知道有 study() 方法但反射不一样它让程序在运行时才决定去操作哪个类// 运行时才知道要操作 Student 这个类 Class? clazz Class.forName(com.example.Student); // 运行时才知道要调用 study 这个方法 Method method clazz.getMethod(study); method.invoke(clazz.newInstance());三、反射的核心APIJava的反射API主要集中在 java.lang.reflect 包下核心就四个类类作用Class代表一个类的元信息是反射的入口Field代表类的成员变量Method代表类的方法Constructor代表类的构造方法四、实战用反射解剖一个类假设我们有一个普通的类public class Person { private String name; public int age; public Person() {} private Person(String name) { this.name name; } public void sayHello() { System.out.println(Hello, Im name); } private void secret() { System.out.println(This is private!); } }获取Class对象的三种方式// 方式1类名.class最常用编译期检查 ClassPerson clazz1 Person.class; // 方式2对象.getClass()已有对象时用 Person p new Person(); Class? extends Person clazz2 p.getClass(); // 方式3Class.forName()动态加载最灵活 Class? clazz3 Class.forName(com.example.Person);获取构造方法并创建对象// 获取所有 public 构造方法 Constructor?[] constructors clazz.getConstructors(); // 获取指定构造方法包括 private ConstructorPerson privateCon clazz.getDeclaredConstructor(String.class); privateCon.setAccessible(true); // 暴力破解访问权限 Person p privateCon.newInstance(Alice);获取并调用方法// 获取 public 方法包括继承的 Method sayHello clazz.getMethod(sayHello); sayHello.invoke(p); // 输出: Hello, Im Alice // 获取 private 方法 Method secret clazz.getDeclaredMethod(secret); secret.setAccessible(true); secret.invoke(p); // 输出: This is private!获取并修改字段// 获取 public 字段 Field ageField clazz.getField(age); ageField.set(p, 20); System.out.println(p.age); // 20 // 获取 private 字段 Field nameField clazz.getDeclaredField(name); nameField.setAccessible(true); nameField.set(p, Bob);五、反射到底有什么用光会API不够要知道什么时候用。反射的典型应用场景框架开发Spring、MyBatis、JUnitSpring 的依赖注入DI就是靠反射实现的// Spring 读取配置文件后大概是这样创建对象的 Class? beanClass Class.forName(com.example.UserService); Object bean beanClass.newInstance(); // 然后反射调用 set 方法注入依赖 Method setDao beanClass.getMethod(setUserDao, UserDao.class); setDao.invoke(bean, new UserDao());动态代理AOP的基础JDK动态代理底层就是反射InvocationHandler handler (proxy, method, args) - { System.out.println(方法 method.getName() 被调用了); return method.invoke(target, args); };序列化与反序列化JSON库如Gson、Jackson通过反射读取对象的字段自动完成对象和JSON字符串的转换。热加载与插件化运行时从外部加载类文件实现不重启程序更新功能。六、反射的代价反射很强大但也有明显的缺点缺点说明性能损耗反射调用比直接调用慢10~100倍JVM难以优化安全性问题setAccessible(true) 可以访问私有成员破坏封装编译期检查失效反射调用的方法名写错了编译不会报错运行时才抛异常代码可读性差一堆字符串硬编码IDE无法跳转维护困难使用建议框架底层可以用业务代码尽量别用。如果非用不可做好缓存Method、Field 对象可以复用。七、一个有趣的实验用反射来打破String的不可变性String s Hello; Field valueField String.class.getDeclaredField(value); valueField.setAccessible(true); char[] value (char[]) valueField.get(s); value[0] h; // 改成小写 System.out.println(s); // 输出: hello 理论上但现代JDK有优化可能不生效