为什么需要反射?
反射让你能在 运行时才决定操作哪个类、哪个方法、哪个字段,而不是在写代码时就确定,Java 反射主要解决这些问题:
1. 动态创建对象
不需要 new,运行时才知道创建哪个类。
2. 动态调用方法
方法名可以是运行时才得知的,比如从数据库、配置文件读取。
3. 动态访问属性
可以直接读写对象的私有字段(不建议滥用)。
4. 构建通用框架
反射是 Spring / Hibernate / MyBatis / Dubbo 等框架的核心。
为什么传统创建类的方式不适用?
传统方式写死:
User user = new User();
user.setName("Tom");
这要求:
- 类名在编译时必须确定
- 要提前知道方法名
- 要提前知道属性名
在很多框架或通用代码里,这些都无法提前确定!
例如:
你写一个工具类,接收任何 Java 对象,转成 JSON,你能提前知道对象是什么类型?
如何使用反射?
通过一个案例进行说明反射的使用过程
这是一个普通的学生类,这时候如果想创建对象进行使用,可以使用Student s = new Student();创建好了之后,便可以拿到对象s,便可以通过对象进行操作。这是常规的操作。
public class Student {
private String name = "张三";
public Student() {
System.out.println("Student构造函数被调用");
}
public String getName() {
System.out.println("getName方法被调用");
return "你好," + name;
}
public void setName(String name) {
this.name = name;
}
}
但是这个这种方式必须提前知道类名、方法名。如果我们是动态的变化,不符合在运行的时候进行动态执行。
假设有这样的场景:
你要执行的类你不知道,执行的方法你也不知道。我通过网络json进行传递给你
{
"class": "com.example.springbootexecutordemo.Student",
"method": "getName"
}
这个告诉你要执行的是com.example.springbootexecutordemo包下的Student类
要执行的方法是getName方法。这时候问题稍微变的有点复杂了,但是这个难不倒聪明的你!
聪明的你,发现问题是在于,在写代码的时候你不知道类的名字,但是在程序要执行的时候是可以知道类的名字,于是你想,要是有一个接口可以通过类的名称就可以创建对象就好了。
有的,有的,有这个API。
先从网络中获取传递的类的名字,方法。
String className = json.get("class");
String methodName = json.get("method");
//1️⃣ 获取类的字节码对象(告诉 Java 要操作谁)
Class<?> clazz = Class.forName(className);
//2️⃣ 创建对象(不写 new Student!)
Object obj = clazz.getDeclaredConstructor().newInstance();
这个时候聪明的你又有疑问了?我创建对象只需要一个步,为啥你需要两步,这是为啥?
- 你写
Student s = new Student()得到的是一个 学生对象。 - 你写
Class<?> clazz = Student.class得到的是一个 学生类的描述信息,也就是“类的说明书”。说明书本身不是学生,它只是帮助你创建学生的蓝图。
再举个简单的例子进行说明,普通 new(直接 new),像去超市买饮料:你直接说“我要可乐”,店员马上给你。
但是反射不一样,反射(Class → 构造器 → newInstance),就像你不知道店员卖啥,于是你:
- 看饮料目录(Class)
- 找到可乐这项(构造器)
- 根据说明书拿货(newInstance)
相信聪明的你很快就能学会了用反射创建对象了。
拿到对象的你,后续的问题便不再是困难
//获取到要执行的方法
Method method = clazz.getMethod(methodName)
//执行反射的方法
method.invoke("调用目标(实例)")
完整的过程
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
// 1️⃣ 获取类对象(告诉 Java 你想操作 Student 类)
Class<?> clazz = Class.forName("com.example.springbootexecutordemo.Student");
// 2️⃣ 创建 Student 对象(不使用 new)
Object obj = clazz.getDeclaredConstructor().newInstance();
// 3️⃣ 获取 getName 方法
Method method = clazz.getMethod("getName");
// 4️⃣ 调用方法
method.invoke(obj);
}
}

反射常用的API
上述是一个简单的反射的使用过程,但是聪明的你又发现了很多问题,万一我不知道类的名字呢?我怎么创建类。这时候反射还提供了其他的方法进行解决这个问题。
获取对象的元信息
1.通过类名
✔ 适合:你代码中就写死了类名(最常用、最快)
Class<Student> clazz = Student.class;
我们来对比下其好处
普通写法(不用反射)
今天假设你有一个工具方法:
public static void printName(Student s) {
System.out.println(s.getName());
}
但如果我想支持 Teacher、Admin、User 呢?
你要写一堆重载:
printName(Teacher t);
printName(Admin a);
printName(User u);
非常麻烦。
使用反射后(类名.class)
public static void printNameByClass(Class<?> clazz) throws Exception {
Object obj = clazz.getDeclaredConstructor().newInstance();
Method m = clazz.getMethod("getName");
System.out.println(m.invoke(obj));
}
调用:
printNameByClass(Student.class);
printNameByClass(Teacher.class);
printNameByClass(Admin.class);
2.通过对象
✔ 适合:你只有对象,不知道是什么类
Student s = new Student();
Class<?> clazz = s.getClass();
|
方式 |
适用场景 |
|
Class.forName() |
不知道类,但有类名字符串(配置、框架) |
|
类名.class |
你知道类(编译期获取 Class) |
|
对象.getClass() |
你只有对象(运行时获取 Class) |
测试完整的代码
【第一部分】三个普通的类(Student / Teacher / Admin)
你可以把它们放在同一个包里。
public class Student {
public String getName() { return "学生:张三"; }
}
public class Teacher {
public String getName() { return "老师:李四"; }
}
public class Admin {
public String getName() { return "管理员:王五"; }
}
【第二部分】普通写法(不用反射)
你必须为每种类型写一个方法:
public class NormalInvoke {
public static void printName(Student s) {
System.out.println(s.getName());
}
public static void printName(Teacher s) {
System.out.println(s.getName());
}
public static void printName(Admin s) {
System.out.println(s.getName());
}
}
❌ 缺点——你需要写很多重复代码
- 每来一种新类型就要再写一个方法
- 同一个逻辑写三遍
- 扩展性差
【第三部分】反射写法:通过类名.class
使用:
Class<Student> clazz = Student.class;
就能拿到该类的元数据。
完整反射版本(核心代码)
import java.lang.reflect.Method;
public class ReflectInvoke {
public static void printNameByClass(Class<?> clazz) throws Exception {
// 1. 创建对象,相当于 new Student() / new Teacher() / new Admin()
Object obj = clazz.getDeclaredConstructor().newInstance();
// 2. 获取 getName 方法
Method method = clazz.getMethod("getName");
// 3. 调用方法
Object result = method.invoke(obj);
System.out.println(result);
}
}
【第四部分】测试类:统一运行两种写法
package com.example.demo;
public class TestCase {
public static void main(String[] args) throws Exception {
System.out.println("===== 普通写法 =====");
NormalInvoke.printName(new Student());
NormalInvoke.printName(new Teacher());
NormalInvoke.printName(new Admin());
System.out.println("\n===== 反射写法(通过类名.class) =====");
ReflectInvoke.printNameByClass(Student.class);
ReflectInvoke.printNameByClass(Teacher.class);
ReflectInvoke.printNameByClass(Admin.class);
}
}
运行结果(你将看到):
===== 普通写法 =====
学生:张三
老师:李四
管理员:王五
===== 反射写法(通过类名.class) =====
学生:张三
老师:李四
管理员:王五
结果一样,但用反射只写了 一个方法。

被折叠的 条评论
为什么被折叠?



