Java反射是JVM运行时暴露类元数据的机制,通过Class及配套API访问已存在结构,不改变字节码;需注意类加载、初始化、访问控制、泛型获取与模块封装等关键细节。
反射本质是 JVM 在运行时暴露的类元数据接口
Java 反射不是“黑魔法”,而是 JVM 把原本编译期就确定的
结构(字段、方法、构造器、注解等)通过
和配套 API 暴露给程序。它不改变字节码,也不绕过访问控制——只是让代码能按名字查到并调用那些本就存在的结构。
常见错误现象:
或
不是反射特有,而是类路径或类加载时机问题;
通常是因为没调用
就去访问 private 成员。
必须确保目标类已被加载(比如已 new 过、被 static 引用过,或显式
)
默认会触发初始化(执行 static 块),
不会——这点在热加载或配置驱动场景里容易踩坑
反射调用方法/字段性能比直接调用低一个数量级,但 JDK 9+ 对
做了内联优化,热点代码影响变小
获取 Class 对象的三种方式及其语义差异
不是所有写法都等价。选错方式会导致类没加载、初始化被跳过,或无法拿到泛型信息。
:最轻量,不触发初始化,适用于已知类名且无需初始化的场景(如类型判断、泛型擦除后取原始类型)
:安全可靠,返回运行时实际类型(可能不是声明类型),适合多态场景下探查真实类型
:会触发初始化,且要求类在 classpath 中;若传入空字符串或拼错类名,抛
注意:
和
的区别在于前者不初始化类,适合延迟加载或避免副作用。
立即学习
“
Java免费学习笔记(深入)
”;
Eclipse导入Android或其他的JAVA项目的正确方法 WORD版
本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
下载
调用私有方法/字段前必须调用 setAccessible(true)
这是反射中最常被忽略的一步。JVM 默认启用封装检查,即使你拿到了
或
对象,不设为可访问就会在
或
时抛
。
该调用只对当前
/
实例生效,不影响其他对象
在安全管理器(SecurityManager)启用的环境(如老版 Applet 或某些容器)中,
可能被拒绝,抛
JDK 12+ 引入了强封装默认策略(
),此时即使调用了
,访问非 open 模块的内部 API 仍会失败——必须加
参数
示例:
—— 缺少第一句,第二句必炸。
反射获取泛型类型信息要靠 getGenericXxx() 系列方法
普通
(如
)返回的是擦除后的原始类型(
而非
)。要拿到带泛型的实际类型,必须用
并处理
子类。
返回
,可能是
(如
)、
(如
)或
(如
)
数组类型要用
,不能用
来反推泛型
泛型信息只保留在源码和字节码中,运行时无法还原匿名内部类或 lambda 表达式里的类型实参
真正难的不是调用,而是安全地向下转型和递归解析嵌套泛型——比如把
拆成三层
树。
反射的复杂性不在语法,而在它把编译期约束全推到了运行时:类是否存在、是否可访问、泛型是否完整、模块是否开放……每一步都可能断裂,而且错误信息往往不指向真正原因。
Classjava.lang.ClassClassNotFoundExceptionNoClassDefFoundErrorIllegalAccessExceptionsetAccessible(true)Class.forName("X")Class.forName()ClassLoader.loadClass()Method.invoke()String.classobj.getClass()Class.forName("java.util.ArrayList")ClassNotFoundExceptionClassLoader.getSystemClassLoader().loadClass("X")forNameFieldMethodget()invoke()IllegalAccessExceptionFieldMethodsetAccessible(true)SecurityException--illegal-access=denysetAccessible(true)--add-opensfield.setAccessible(true); field.set(obj, "new value");getXxxType()getReturnType()ListListgetGenericXxx()TypeMethod.getGenericReturnType()TypeParameterizedTypeListWildcardType? extends NumberTypeVariableTgetGenericComponentType()getComponentType()Map> Type