【无标题】

Java反射机制入门

1. 引入:为什么需要反射?

1.1 正常创建对象的局限性

正常情况下,我们创建对象是这样的:

java 复制代码
Student s = new Student();
s.study();

问题 :这种写法在编译时就必须知道类的全名。如果类的名字是在程序运行时才知道(比如从配置文件读取),怎么办?

properties 复制代码
# config.properties
className = com.example.Student
1.2 反射的解决方案

反射 允许程序在运行时动态地获取类的信息、创建对象、调用方法。

java 复制代码
// 运行时才知道要创建哪个类
String className = 从配置文件读取到的类名;
Class clazz = Class.forName(className);  // 获取类对象
Object obj = clazz.newInstance();        // 创建实例

核心理解:反射就是把Java类本身当成对象来操作。


2. 类的加载机制

2.1 类加载的三个阶段
复制代码
编写代码 → 编译 → 加载 → 连接 → 初始化 → 使用 → 卸载
          ↓
      .java  .class
阶段 说明
加载 将.class文件读入内存,创建Class对象
连接 验证、准备(分配内存给静态变量)、解析(符号引用转直接引用)
初始化 执行静态代码块和静态变量赋值
2.2 类加载器(ClassLoader)

类加载器负责把.class文件加载到内存:

java 复制代码
// 获取类加载器
ClassLoader loader = Student.class.getClassLoader();
System.out.println(loader);  // AppClassLoader

// 类加载器的层级
// BootstrapClassLoader(根加载器,加载rt.jar)
//      ↑
// ExtensionClassLoader(扩展加载器)
//      ↑
// AppClassLoader(应用加载器,加载自己写的类)
2.3 类加载的时机

类第一次被使用时才会加载:

  • new对象时
  • 调用静态方法/静态变量时
  • 反射调用时
java 复制代码
// 下面这行代码会触发Student类加载
Student s = new Student();

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

方式 代码 适用场景
类名.class Class c = Student.class; 已知类型
对象.getClass() Class c = s.getClass(); 已有对象
Class.forName() Class c = Class.forName("com.example.Student"); 只知道全类名(最常用)
java 复制代码
public class GetClassDemo {
    public static void main(String[] args) throws Exception {
        // 方式1:类名.class
        Class c1 = Student.class;
        
        // 方式2:对象.getClass()
        Student s = new Student();
        Class c2 = s.getClass();
        
        // 方式3:Class.forName()(最灵活)
        Class c3 = Class.forName("com.Student");
        
        System.out.println(c1 == c2);  // true
        System.out.println(c1 == c3);  // true(同一个Class对象)
    }
}

重点:同一个类在JVM中只存在一个Class对象,三种方式获取的是同一个。


4. 获取类的结构信息

4.1 获取构造方法
java 复制代码
import java.lang.reflect.Constructor;

public class ConstructorDemo {
    public static void main(String[] args) throws Exception {
        Class clazz = Student.class;
        
        // 获取所有public构造方法
        Constructor[] constructors = clazz.getConstructors();
        
        // 获取所有构造方法(包括private)
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        
        // 获取指定参数的public构造方法
        Constructor c1 = clazz.getConstructor(String.class, int.class);
        
        // 获取指定参数的构造方法(包括private)
        Constructor c2 = clazz.getDeclaredConstructor(String.class);
        
        //获取构造方法的修饰符号
        int modefiy = c2.getModifiers();
        
        //获取所有参数
        Parameter[] parameters = c2.getParameters();
        
        // 通过构造方法创建对象
        Student s = (Student) c1.newInstance("张三", 18);
        
        // 如果是private构造方法,需要先设置可访问
        c2.setAccessible(true);
        Student s2 = (Student) c2.newInstance("李四");
    }
}
4.2 获取成员变量
java 复制代码
import java.lang.reflect.Field;

public class FieldDemo {
    public static void main(String[] args) throws Exception {
        Class clazz = Student.class;
        
        // 获取所有public成员变量
        Field[] fields = clazz.getFields();
        
        // 获取所有成员变量(包括private)
        Field[] declaredFields = clazz.getDeclaredFields();
        
        // 获取指定的public成员变量
        Field field1 = clazz.getField("name");
        
        // 获取指定的成员变量(包括private)
        Field field2 = clazz.getDeclaredField("age");
        
        //获取成员修饰符
        int modifiy = field2.getModifiers();
        
        //获取名字
        String name = field2.getName();
        
        //获取数据类型
        Class<?> type = field2.getType();
        
        // 操作成员变量
        Student s = new Student("张三", 18);
        
        // 获取值
        Object name = field1.get(s);
        
        // 设置值
        field1.set(s, "李四");
        
        // private变量需要先设置可访问
        field2.setAccessible(true);
        field2.set(s, 20);  // 给age赋值
    }
}
4.3 获取成员方法
java 复制代码
import java.lang.reflect.Method;

public class MethodDemo {
    public static void main(String[] args) throws Exception {
        Class clazz = Student.class;
        
        // 获取所有public方法(包括父类的)
        Method[] methods = clazz.getMethods();
        
        // 获取所有方法(包括private,不包括父类)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        
        // 获取指定的public方法
        Method method1 = clazz.getMethod("study", String.class);
        
        //获取方法修饰符
        int modefiers = method1.getModifiers();
        
        //获取方法名字
        String name = method1.getName();
        
        //获取所有参数
        Parameter[] parameters = method1.getParameters();
        
        // 获取指定的方法(包括private)
        Method method2 = clazz.getDeclaredMethod("privateMethod");
        
        //获取方法抛出的异常
        Class[] = exceptions = method1.getExceptionTypes();
        
        // 调用方法
        Student s = new Student("张三", 18);
        
        // 调用public方法
        Object result = method1.invoke(s, "语文");  // 相当于 s.study("语文")
        
        // 调用private方法
        method2.setAccessible(true);
        method2.invoke(s);
        
        // 调用静态方法(不需要传对象)
        Method staticMethod = clazz.getMethod("staticMethod");
        staticMethod.invoke(null);
    }
}
4.4 获取其他信息
java 复制代码
public class OtherInfoDemo {
    public static void main(String[] args) {
        Class clazz = Student.class;
        
        // 获取类名
        String className = clazz.getName();           // 全类名:com.example.Student
        String simpleName = clazz.getSimpleName();    // 简单类名:Student
        
        // 获取包名
        Package pkg = clazz.getPackage();
        String pkgName = pkg.getName();               // com.example
        
        // 获取父类
        Class superClass = clazz.getSuperclass();     // Person.class
        
        // 获取实现的接口
        Class[] interfaces = clazz.getInterfaces();   // [Comparable.class, Serializable.class]
        
        // 判断类型
        boolean isArray = clazz.isArray();            // 是否是数组
        boolean isInterface = clazz.isInterface();    // 是否是接口
        boolean isEnum = clazz.isEnum();              // 是否是枚举
    }
}

5. 反射常用API总结

5.1 Class类的方法
方法 说明
Class.forName(String) 获取Class对象
newInstance() 调用无参构造创建对象(已过时)
getConstructor(Class...) 获取public构造方法
getDeclaredConstructor(Class...) 获取指定构造方法
getMethod(String, Class...) 获取public方法
getDeclaredMethod(String, Class...) 获取指定方法
getField(String) 获取public成员变量
getDeclaredField(String) 获取指定成员变量
getName() 获取全类名
getSuperclass() 获取父类
5.2 Constructor类的方法
方法 说明
newInstance(Object...) 创建对象
setAccessible(boolean) 设置可访问(突破private)
5.3 Method类的方法
方法 说明
invoke(Object, Object...) 调用方法(传对象和参数)
setAccessible(boolean) 设置可访问
getName() 获取方法名
5.4 Field类的方法
方法 说明
get(Object) 获取成员变量值
set(Object, Object) 设置成员变量值
setAccessible(boolean) 设置可访问
getType() 获取变量类型

6. 反射的应用场景

场景 说明
框架开发 Spring、MyBatis通过反射创建对象、注入属性
配置文件驱动 从配置文件读取类名,动态创建
注解处理 读取注解信息,执行相应逻辑
调试工具 查看任意对象的结构

7. 反射的优缺点

优点 缺点
动态性,运行时决定行为 性能较低(比直接调用慢)
框架的基础 破坏封装(private可访问)
高度灵活性 代码可读性下降
解耦 安全性降低

8. 易错点总结

  1. Class.forName()要写全类名 :包含包名,如com.example.Student

  2. newInstance()已过时 :Java 9后用getDeclaredConstructor().newInstance()

  3. private成员需要setAccessible(true) :否则会报IllegalAccessException

  4. invoke静态方法时对象参数传null :method.invoke(null, 参数)

  5. 基本类型反射要用Type :int.class而不是Integer.class

  6. 性能问题:高频调用的代码避免使用反射,或使用缓存


9. 对比总结

维度 正常调用 反射调用
时机 编译时 运行时
性能 快 慢
灵活性 低 高
封装性 遵守 可破坏
代码安全 高 低
适用场景 业务代码 框架代码

相关推荐
SL_staff38 分钟前
从邮箱传文档到协同流重构:企业文档协作的系统性破局实践
java·开源·github
smallyoung2 小时前
告别N+1:Spring Boot 一键数据翻译框架体系
java·后端·架构
Bazingga4 小时前
Java16 record 完整拆解——语法、语义、限制、反射
java·后端
SL_staff5 小时前
规则联动:给工厂装上‘自动化神经反射弧’——JVS·物联网平台规则引擎实战教程
java·人工智能·物联网
骑着蜗牛撵大象3276 小时前
Linux 进程间通信之管道:从 pipe() 的内核语义到 FIFO 落地排错
android·java·linux·系统编程·fifo·管道·进程间通信
WJ.Polar6 小时前
Java封装、继承、多态
java·开发语言
ly76896 小时前
PostgreSQL 备份恢复策略:pg_basebackup、WAL 归档与 PITR 在企业 Java 系统中的落地
java·数据库·postgresql·pitr·pg_basebackup·wal 归档
摇滚侠6 小时前
Java 中如何表示负数,有符号和无符号的区别,补码的计算规则,char byte short int long 的范围
java·开发语言
weixin_307779136 小时前
C++代码实现MATLAB中的fitrtree函数功能
开发语言·c++·算法·matlab
奇牙coding7 小时前
Codex CLI 升级后多文件编辑静默失效:config.yaml 枚举变更排查与修复指南
java·前端·javascript·ai