Class.forName()在模块化环境中失败的根本原因是模块系统限制类可见性,需确保目标模块导出包、调用方模块声明requires依赖,并使用模块感知的类加载器;否则即使类存在也会抛ClassNotFoundException。
模块化环境下用 class.forname() 加载类失败,核心问题不是方法本身出错,而是模块系统(java 9+ 的 module system)对类可见性、包导出和类加载器协作做了严格限制。默认情况下,即使类存在,若所在模块未导出对应包,或调用方模块未声明对其依赖,class.forname() 就会抛 classnotfoundexception —— 这和传统 classpath 环境下的“找不到类”表现相似,但根源完全不同。
确认模块是否导出目标包
Java 模块默认不导出任何包。如果目标类在模块 com.example.lib 中,而它的包是 com.example.service,那么必须在该模块的 module-info.java 中显式导出:
exports com.example.service;- 若需被反射访问(包括
Class.forName()),还建议加上opens(尤其涉及私有成员反射时):opens com.example.service;
未导出 = 模块外不可见,哪怕类文件物理存在,Class.forName() 也查不到。
检查调用方模块是否声明了 requires
使用 Class.forName() 的代码所在模块,必须在自己的 module-info.java 中声明对目标模块的依赖:
requires com.example.lib;- 如果只是编译期需要、运行时可能不存在,可用
requires static;但动态加载场景一般需直接requires
缺少 requires 声明,JVM 在模块解析阶段就会拒绝链接,反射调用自然失败。
避免跨模块使用上下文类加载器陷阱
模块化后,不同模块可能由不同类加载器(如 ModuleLayer.boot().findLoader(...))管理。默认 Class.forName(String) 使用当前类的类加载器,它通常只看到本模块及 requires 的模块 —— 但若目标类在未声明依赖的模块中,或通过自定义类加载器加载,就容易失败。
稳妥做法是显式指定模块感知的类加载器:
- 获取目标模块的类加载器:
ClassLoader loader = ModuleLayer.boot().findModule("com.example.lib").get().getClassLoader(); - 再调用:
Class.forName("com.example.service.MyService", true, loader);
注意:不能直接用 Thread.currentThread().getContextClassLoader(),它在模块化应用中往往不携带模块上下文。
区分 ClassNotFoundException 和 IllegalAccessError
模块化下常见混淆:类能成功加载(Class.forName() 不抛异常),但在后续反射调用(如 newInstance() 或 getMethod())时抛 IllegalAccessError 或 InaccessibleObjectException。
这说明类加载成功,但访问被模块系统拦截。此时应检查:
- 目标模块是否
opens对应包(不只是exports) - 是否在 JVM 启动参数中添加了必要开放指令,例如:
--add-opens com.example.lib/com.example.service=ALL-UNNAMED - 若用的是 JDK 17+,默认强封装更严,
--add-opens几乎成为反射必需项
这类错误不是 Class.forName() 的问题,但常被误判为加载失败。

















