【无标题】

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静态方法时对象参数传nullmethod.invoke(null, 参数)

  5. 基本类型反射要用Typeint.class而不是Integer.class

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


9. 对比总结

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

相关推荐
落寞的星星13 分钟前
这个对象就包含了已经转换好的DFA和各种词法分析器运转所需要的参数。下一步,我们就可以用ScannerInfo对象创建出Scanner对象,请看下面的代码:
开发语言·c#
nothing&nowhere33 分钟前
用 Python 做问卷数据清洗:无效样本检测与处理实战
开发语言·python·数据清洗·数据处理·问卷星·问卷星脚本·刷问卷
2601_961593421 小时前
Rust 开发环境配置繁琐?RustRover 开箱即用搞定编码调试
开发语言·后端·macos·rust
HZZD_HZZD2 小时前
DL/T 645-2026新国标深度解读与智能电表协议适配实战:从帧结构变化到Java采集器升级的全链路改造方案
java·开发语言
多加点辣也没关系4 小时前
JavaScript|第4章:类型转换
开发语言·javascript
聪慧的水蜜桃4 小时前
【YFIOs】用C#开发硬件之设备上云
开发语言·c#
yqcoder4 小时前
httpOnly 是什么,又有什么用?
开发语言·前端·javascript
山登绝顶我为峰 3(^v^)34 小时前
C/C++ 交叉编译方法
java·c语言·c++
一叶飘零_sweeeet4 小时前
Codex 与 Claude Code 深度拆解:两代 AI 编程智能体的技术本质与 Java 实战指南
java·ai·codex·claude code