Java反射:一招解决多类调用难题

为什么需要反射?

反射让你能在 运行时才决定操作哪个类、哪个方法、哪个字段,而不是在写代码时就确定,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),就像你不知道店员卖啥,于是你:

  1. 看饮料目录(Class)
  2. 找到可乐这项(构造器)
  3. 根据说明书拿货(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) =====
学生:张三
老师:李四
管理员:王五

结果一样,但用反射只写了 一个方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值