JDK16及以上版本如何在自有Java代码中调用Java工具类静态方法
问题错误原因
Unable to make public static void sun.tools.jar.Main.main(java.lang.String[]) accessible: module jdk.jartool does not "exports sun.tools.jar" to unnamed module @46d21ee0
该报错是JDK 16及以上版本对JDK内部API强封装导致的:JDK 9引入的JPMS模块化系统将sun.tools.jar.Main归属到jdk.jartool模块,且该类属于模块内部未导出的非公开API,JDK 16默认禁止类路径下的未命名模块访问这类内部API,直接反射调用就会触发权限错误。
可行解决方案
方案1:子进程调用官方工具命令(生产环境首选,无兼容性风险)
放弃直接在代码中反射调用内部API的实现方式,改为通过ProcessBuilder启动子进程执行JDK自带的jar、javac等官方命令,该方案兼容所有JDK版本,不依赖任何内部实现。
参考代码示例:
// jar 命令调用示例 public int runJarCommand(String[] args) throws IOException, InterruptedException { // 构造命令,args为原本传入sun.tools.jar.Main的参数 ProcessBuilder pb = new ProcessBuilder(); pb.command("jar", args); // 可配置工作目录、IO输出规则 pb.inheritIO(); Process p = pb.start(); return p.waitFor(); }
执行后通过返回码判断执行结果,0为成功,非0为失败。
方案2:添加JVM启动参数临时开放权限(适合临时调试)
如果暂时无法修改代码,可以在启动应用时添加JVM启动参数,手动开放对应模块的导出权限:
--add-exports jdk.jartool/sun.tools.jar=ALL-UNNAMED
如果同时调用了Java编译器的内部类,追加对应模块的导出参数:
--add-exports jdk.compiler/com.sun.tools.javac.*=ALL-UNNAMED
该方案属于临时适配方案,后续更高版本JDK可能完全封禁内部API访问权限,存在长期兼容风险。
方案3:使用JDK公开API实现对应能力
- 调用Java编译器可直接使用
javax.tools.ToolProvider.getSystemJavaCompiler()公开API,不需要依赖内部的javac实现类。 - 打包jar文件可以使用
java.util.jar.JarOutputStream等公开类自行实现打包逻辑,完全脱离对sun.tools.jar的依赖。
内容的提问来源于stack exchange,提问作者user2305886
相关产品推荐
相关产品推荐

