Java反射机制依赖类加载完成后生成的Class对象,不参与加载过程;获取Class对象有4种方式:类名.class(不初始化)、对象.getClass()(已实例化)、Class.forName()(触发初始化)、ClassLoader.loadClass()(仅加载不初始化)。

Java 类加载过程中,反射机制本身并不参与类加载的执行(比如验证、准备、解析、初始化),而是依赖类加载完成后生成的 Class 对象来动态获取类信息。换句话说:反射不是“在加载过程中”介入,而是在类已加载进 JVM 后,通过 Class 对象反向读取其元数据。
关键在于理解这个时序关系:类加载 → 生成唯一 Class 对象 → 反射 API 通过该对象访问信息。
✅ 获取 Class 对象的 4 种方式(反射的起点)
只有拿到 Class 对象,才能开始反射操作。常见方式如下:
类名.class
编译期已知类型,安全高效,不触发类初始化。Classclazz = User.class; 对象.getClass()
已有实例时使用,返回运行时实际类型(支持多态)。User u = new User(); Class> clazz = u.getClass();Class.forName("全限定类名")
最常用动态加载方式,会触发类的初始化(执行静态块)。Class> clazz = Class.forName("com.example.User");ClassLoader.loadClass("全限定类名")
不会触发初始化,适合需要延迟初始化的场景。Class> clazz = getClass().getClassLoader().loadClass("com.example.User");
⚠️ 注意:
forName和loadClass的区别常被忽略——前者执行,后者只加载不初始化。
? 通过 Class 对象获取类结构信息
一旦获得 Class 实例,即可调用其方法提取各类元数据:
基本信息
getName()、getSimpleName()、getSuperclass()、getInterfaces()、getModifiers()-
构造器
getConstructor(...)(仅 public)getDeclaredConstructor(...)(含 private,需setAccessible(true)才能调用) 方法
getMethod("name", paramTypes...)→ 查找 public 方法(含父类)getDeclaredMethod("name", paramTypes...)→ 查找本类所有方法(含 private)字段
getField("name")→ 仅 public 字段(含继承)getDeclaredField("name")→ 本类所有字段(含 private),同样需setAccessible(true)才能读写注解与泛型
getAnnotations()、getDeclaredAnnotations()getGenericSuperclass()、getTypeParameters()等支持泛型反射
? 反射如何“突破封装”?
Java 的访问控制(如 private)是编译期检查,JVM 运行时并不强制限制。反射通过 AccessibleObject.setAccessible(true) 关闭语言层访问检查,从而读写私有成员:
Field field = clazz.getDeclaredField("password");
field.setAccessible(true); // 关键一步
field.set(obj, "newpwd");
这本质上是 JVM 提供的底层能力,不是绕过 JVM 安全模型,而是明确启用受控的反射访问。
? 实际开发中要注意什么?
-
性能开销:反射调用比直接调用慢,尤其频繁使用
setAccessible(true)或反复getMethod。建议缓存Method/Field对象。 -
安全性:
setAccessible(true)在 SecurityManager 启用时可能抛出异常;模块化(Java 9+)下还需--add-opens参数开放包。 -
稳定性:依赖字符串名称(如
"setName")易出错,缺乏编译期校验,建议配合注解或配置中心降低硬编码风险。 - 兼容性:某些框架(如 Spring、MyBatis)内部大量使用反射,但业务代码应避免过度暴露私有细节,防止重构断裂。
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











