【从0开始学习Java | 第22篇】反射

文章目录

Java反射:从基础到框架应用的实战指南

一、反射介绍

1. 什么是反射?

简单来说,反射是Java提供的一种能力:允许程序在运行时获取类的详细信息(如属性、方法、构造器等),并动态操作这些信息

正常情况下,我们使用类的流程是"编译期确定类 → 实例化 → 调用方法",比如:

java 复制代码
User user = new User(); // 编译期已知User类
user.setName("张三");

而反射则是"运行时获取类信息 → 动态操作",即使编译期不知道具体类名,也能通过字符串(如配置文件中的类路径)完成对象创建和方法调用。

2. 为什么需要反射?

举个实际开发中的例子:假设你需要开发一个工具,根据用户配置的类名创建对象。如果没有反射,只能硬编码判断:

java 复制代码
// 配置文件中读取的类名
String className = "com.example.Student"; 

// 不使用反射:必须提前知道所有可能的类,扩展性极差
if (className.equals("com.example.User")) {
    return new User();
} else if (className.equals("com.example.Student")) {
    return new Student();
} else {
    // 新增类时必须修改代码
}

而有了反射,只需两行代码即可动态处理任意类:

java 复制代码
Class<?> clazz = Class.forName(className); // 运行时加载类
Object obj = clazz.newInstance(); // 动态创建实例

这就是反射的核心价值:摆脱编译期的类型依赖,让程序更灵活、更具扩展性

二、反射的核心:Class类

反射的所有操作都围绕java.lang.Class类展开。每个类被JVM加载后,都会生成一个唯一的Class对象,它包含了该类的所有信息(属性、方法、构造器等)。可以说,Class对象是反射的"入口"。

1. 获取Class对象的三种方式

要使用反射,第一步是获取目标类的Class对象,常用三种方式:

方式1:通过 Class.forName(全类名)(运行时动态获取)

最常用的方式,通过类的全限定名(包名+类名)动态加载,适合从配置文件或数据库中读取类名的场景:

java 复制代码
// 需处理ClassNotFoundException(类不存在时抛出)
Class<?> clazz = Class.forName("com.example.User"); 
方式2:通过 类名.class(编译期已知类)

如果编译期就知道具体类,直接通过类名.class获取,无需处理异常:

java 复制代码
Class<User> userClass = User.class;
Class<String> stringClass = String.class;
方式3:通过 对象.getClass()(已有实例)

如果已有对象实例,调用其getClass()方法:

java 复制代码
User user = new User();
Class<?> clazz = user.getClass(); // 此时clazz即为User类的Class对象

注意:Class.forName()会触发类的初始化(执行静态代码块),而后两种方式仅加载类不初始化。

三、反射实战:操作类的结构

我们以一个Student类为例,演示如何通过反射操作构造器、属性和方法:

java 复制代码
import java.io.IOException;

public class Student {
    private String name;
    private int age;
    public String gender;

    public Student() {

    }

    public Student(String name) {
        this.name = name;
    }

    protected Student(int age) {
        this.age = age;
    }

    private Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void sleep(){
        System.out.println("睡觉");
    }

    private void eat(String something) throws  IllegalAccessError , IOException {
        System.out.println("在吃"+something);
    }

}

1. 利用反射获取构造方法(Constuctor)

通过反射调用构造器创建对象,支持无参和有参构造,包括私有构造器。

使用的方法
代码示例
java 复制代码
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;

public class MyReflect_Constructor {
    public static void main(String []args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        // 1.获取class字节码文件对象
        Class clazz = Class. forName("Student");

        // 2.获取所有公共构造方法
        Constructor[] cons = clazz.getConstructors();
        for(Constructor con : cons){
            System.out.println(con);
        }
        System.out.println("==============");

        // 获取所有构造方法,包括私有
        Constructor[]cons2 =   clazz.getDeclaredConstructors();
        for(Constructor con : cons2){
            System.out.println(con);
        }

        System.out.println("==============");

        Constructor con3 = clazz.getConstructor();
        System.out.println(con3);

        Constructor con4 = clazz.getDeclaredConstructor(int.class);
        System.out.println(con4);

        Constructor con5 = clazz.getConstructor(String.class);
        System.out.println(con5);

        Constructor con6 = clazz.getDeclaredConstructor(String.class,int.class);
        System.out.println(con6);

        // 获取权限修饰符
        int modifiers =  con6.getModifiers();
        System.out.println(modifiers);

        // 获取对应构造方法的所有参数
        Parameter[] parameters = con6.getParameters();
        for(Parameter parameter :parameters){
            System.out.println(parameter);
        }

        // 暴力反射:临时取消权限的校验 --> 利用私有构造方法创建对象
        con6.setAccessible(true);
        Student stu = (Student)con6.newInstance("林七夜",18);
        System.out.println(stu);
    }
}

运行结果:

复制代码
public Student(java.lang.String)
public Student()
==============
private Student(java.lang.String,int)
protected Student(int)
public Student(java.lang.String)
public Student()
==============
public Student()
protected Student(int)
public Student(java.lang.String)
private Student(java.lang.String,int)
2
java.lang.String arg0
int arg1
Student@4554617c

方法区别:getConstructor()只能获取public构造器,getDeclaredConstructor()可获取所有权限的构造器。

2. 利用反射获取成员变量(Field)

反射可以获取类的所有属性(包括私有),并读写其值。

使用的方法
代码示例
java 复制代码
import java.lang.reflect.Field;

public class MyReflect_Field {
    public static void main(String []args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        // 1.获取class字节码文件的对象
        Class clazz = Class.forName("Student");

        // 2.获取成员变量
            // 公共的
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        System.out.println("----------");

            // 所有的
        Field[] fields2 = clazz.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field);
        }

        System.out.println("=========");

        // 3.获取单个成员变量
            // 私有
        Field name = clazz.getDeclaredField("name");
        System.out.println(name);

        System.out.println("---------");
            //公共
        Field gender = clazz.getField("gender");
        System.out.println(gender);

        // 4.获取权限修饰符
        int modifiers = name.getModifiers();
        System.out.println(modifiers);

        // 5.获取成员变量的名字
        String n = name.getName();
        System.out.println(n);

        // 6.获取成员变量的数据类型
        Class<?> type  = name.getType();
        System.out.println(type);

        // 7.获取成员变量记录的值
        Student s = new Student("张三",18,"男");
        name.setAccessible(true);
        String value = (String) name.get(s);
        System.out.println(value);

        // 8.修改对象里面的值
        name.set(s,"李四");
        System.out.println(s);

    }
}

3. 调用方法(Method)

反射可以调用类的任意方法(包括私有方法),支持传递参数。

使用的方法
代码示例
java 复制代码
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class MyReflect_Method {
    public static void main(String []args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        // 1.获取class字节码文件对象
        Class clazz = Class.forName("Student");

        // 2.获取里面所有的方法对象(包含父类中的所有公共方法)

        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        System.out.println("-------------");

        // 3. 获取子类的所有方法(不包括父类的,但包括本类的私有方法)
        Method[] methods2 = clazz.getDeclaredMethods();
        for (Method method : methods2) {
            System.out.println(method);
        }

        // 4.获取单一指定方法
        Method m = clazz.getDeclaredMethod("eat",String.class);
        System.out.println(m);

        // 5.获取方法的修饰符
        int modifiers = m.getModifiers();
        System.out.println(modifiers);

        // 6.获取方法的名字
        System.out.println(m.getName());

        // 7.获取方法的形参
        Parameter[] parameters = m.getParameters();
        for (Parameter parameter : parameters) {
            System.out.println(parameter);
        }

        // 8.获取方法抛出的异常
        Class[] exceptionTypes = m.getExceptionTypes();
        for (Class exceptionType : exceptionTypes) {
            System.out.println(exceptionType);
        }

        // 9.Method类中用于创建对象的方法 (重点)
        Student s = new Student();
        m.setAccessible(true);
        // 参数一s:表示方法的调用者
        // 参数二"汉堡包":表示在调用方法是传递的实际参数
        m.invoke(s,"汉堡包");
    }
}

方法区别:getMethod()获取public方法,getDeclaredMethod()获取所有权限的方法;invoke()的第一个参数是实例(静态方法传null),后续参数是方法的实际参数。

四、反射的应用场景

反射的灵活性使其成为框架设计的核心,但日常业务开发中需谨慎使用(避免过度设计)。常见应用场景包括:

  1. 框架的IOC容器:如Spring通过反射根据配置文件创建Bean,实现"控制反转";
  2. ORM框架:如MyBatis通过反射将数据库查询结果映射到Java对象的属性;
  3. 动态代理:AOP的实现依赖反射调用目标方法(如事务增强、日志记录);
  4. 注解解析 :自定义注解(如@Controller@RequestMapping)的生效需要反射扫描并处理;
  5. 序列化/反序列化:JSON工具(如Jackson)通过反射将JSON字符串转换为Java对象。

五、反射的优缺点

优点:

  • 动态性:运行时操作类,适应灵活配置场景(如通过配置文件切换实现类);
  • 解耦:框架与业务类通过反射交互,无需硬编码依赖,降低耦合度;
  • 通用性:一套反射代码可处理任意类,提升工具类的复用性。

缺点:

  • 性能损耗:反射需要解析字节码,调用效率比直接调用低(但JVM已优化,非高频场景可忽略);
  • 安全风险setAccessible(true)会绕过访问权限检查,可能破坏类的封装性;
  • 可读性差:反射代码较抽象,调试和维护成本高于直接调用。

如果我的内容对你有帮助,请 点赞 , 评论 , 收藏 。创作不易,大家的支持就是我坚持下去的动力!

相关推荐
superlls3 小时前
(Spring)Spring Boot 中 @Valid 与全局异常处理器的联系详解
java·spring boot·后端
hmbbcsm3 小时前
python学习之路(四)
学习
Greedy Alg3 小时前
Socket编程学习记录
网络·websocket·学习
一念&3 小时前
每日一个C语言知识:C 字符串
c语言·开发语言
知识分享小能手4 小时前
uni-app 入门学习教程,从入门到精通,uni-app 基础知识详解 (2)
前端·javascript·windows·学习·微信小程序·小程序·uni-app
0110_10244 小时前
tauri + rust的环境搭建---初始化以及构建
开发语言·后端·rust
会开花的二叉树4 小时前
C++微服务 UserServer 设计与实现
开发语言·c++·微服务
像风一样自由20204 小时前
Rust Tokio vs Go net/http:云原生与嵌入式生态选型指南
开发语言·golang·rust
DuHz4 小时前
C程序中的数组与指针共生关系
linux·c语言·开发语言·嵌入式硬件·算法