☰
IDEA打包jar报“找不到或无法加载主类”的根因与解决指南
2026/10/22 7:43:05 网站建设 项目流程

简介:面向Java开发者的IDEA打包排错资料,专注解决使用IntelliJ IDEA将项目打包为可执行JAR时常见的“找不到或无法加载主类 main”问题。资源以单个PDF文档形式提供,压缩包仅258KB,内容紧凑,能够快速定位核心矛盾。文档从问题根源讲起,分Maven方式与IDEA Build模式两条路线展开:Maven部分演示了maven-jar-plugin中mainClass与addClasspath的正确配置,并说明主类全限定名填写要点;IDEA部分则指导通过Project Structure下的Artifacts配置主类并完成构建。同时,针对Windows环境还给出了CLASSPATH环境变量的排查与处理建议,包括添加当前目录或直接删除变量等操作细节。资料目前已有20877人学习下载,适合遇到同类报错的前后端开发人员、Maven初学者及需要快速打包部署的工程师,照着示例调整即可有效规避入口类加载异常。 从标题看,这是一篇讲 IntelliJ IDEA 打包 jar 后运行报“找不到或无法加载主类”的实战笔记。需要把这类问题的完整链路讲透:编译产物在哪、MANIFEST.MF 怎么配、fat jar 和普通 jar 的区别、IDEA 里几种打包方式的差异、运行时 Class-Path 怎么写、以及最常见的“代码能跑但 java -jar 起不来”的根因。

目录结构(6 章)

1. 这个报错不是玄学:先搞清“主类”在打包链路里丢在哪一环

2. 为什么会“找不到或无法加载主类”:JVM 加载主类的三个硬性条件

2.1 条件一:MANIFEST.MF 里的 Main-Class 必须写对

2.2 条件二:类的全限定名和实际编译产物要对得上

2.3 条件三:lib 依赖没进包,主类加载了但依赖缺失

3. 用 IDEA 打包 jar:两种可靠方式与最小操作步骤

3.1 方式一:Artifacts + Build 菜单打包(适合无第三方依赖的项目)

3.2 方式二:Maven 插件打包(适合多模块和第三方依赖项目)

3.3 打包后先做“本地冒烟验证”:java -jar 之前先看包内结构

4. IDEA 打包后运行报错的 5 条踩坑与排查记录

4.1 报错“找不到或无法加载主类”但代码在 IDEA 里能跑

4.2 Manifest 文件里 Main-Class 末尾多了空格或回车

4.3 用 Maven 打包后 jar 包只有几 KB,运行时必然起不来

4.4 提示“NoClassDefFoundError”而不是“主类找不到”

4.5 打出来的 jar 在命令行能跑,双击却不行

5. 从“能打包”到“能交付”:三个必查的验证项与参数设置

5.1 用 java -jar 跑通三个前置检查

5.2 Build 菜单里的 “Build Artifacts” 和 “Rebuild” 区别

5.3 交付前检查清单:JDK 版本、控制台输出、退出码

6. 把“一键打包”做成肌肉记忆:自定义运行配置与后续排错习惯

以下是正文:


1. 这个报错不是玄学:先搞清“主类”在打包链路里丢在哪一环

在 IDEA 里点了半天“Build Artifacts”,控制台显示 BUILD SUCCESSFUL,兴冲冲跑到 target 目录执行java -jar xxx.jar,结果一行刺眼的错误: 找不到或无法加载主类 com.example.Main糊在脸上。这个报错不是玄学,也不是 JDK 坏了,而是“源代码能跑”和“jar 包能跑”之间隔着一整套编译、打包、类加载链路,问题几乎总是出在 MANIFEST.MF 里的 Main-Class 配错、类的全限定名和编译产物不匹配、或者依赖没有跟着打进去这三件事上。

这个标题真正要解决的,就是让你在 IDEA 里从“写出能跑的 main 方法”推进到“打出能直接java -jar跑起来的 jar”。它适合两类读者:一类是刚接触 Java 项目交付、还在用 IDEA 默认配置打 jar 的开发者;另一类是已经能打出 jar、但一换机器或一换目录结构就翻车,想知道怎么彻底告别这个报错的人。下面从前提到实操,一步步把这层窗户纸捅破。

2. 为什么会“找不到或无法加载主类”:JVM 加载主类的三个硬性条件

“找不到或无法加载主类”这句话在 JVM 里的实际含义是:java启动器根据你传入的类名去 classpath 里找这个类,要么没找到(找不到),要么找到了但加载过程中出错(无法加载)。先说一个常见误区:很多人以为是 IDEA 没把代码编译进去,但其实绝大多数情况是编译产物存在、入口类也存在,只是 JVM 在启动时拿到的“入口类全限定名”和“依赖路径”是错的。

2.1 条件一:MANIFEST.MF 里的 Main-Class 必须写对

jar 包的本质是一个 zip 文件,里面的META-INF/MANIFEST.MF是一个描述文件。你执行java -jar xxx.jar时,JVM 会先读这个文件里的Main-Class字段,把它当作启动入口。这个字段的值必须是“类的全限定名”,也就是包名加类名,比如com.example.Main,不能带.class后缀,也不能写Main.java。

用 IDEA 的 Artifacts 方式打包时,IntelliJ IDEA 会自动生成 MANIFEST.MF,并在你选择 Main Class 后把值填进去。这里的坑在于:如果你在 Project Structure 里改了模块名或移动了类文件包路径,旧的 Artifacts 配置仍然指向原来的类名,打包时不会自动同步,产物里的 MANIFEST.MF 就维持着旧值。这时 JVM 按旧类名去查,当然找不到。

2.2 条件二:类的全限定名和实际编译产物要对得上

Main-Class写对了,还得保证 classpath 里真的存在这个类。这里要分清两个目录:编译输出目录和 jar 包。IDEA 里默认的编译输出是out/production/<模块名>/,当你构建 Artifacts 时,IDEA 会把编译产物拷贝进 jar 包并按包结构放好。如果你的模块里有多个源码根目录(比如 src 和 test),或者用了 Lombok 之类的注解处理器导致源码里的类没被真正编译成 .class 文件,那最终 jar 包里的类路径和 MANIFEST 里的类名就对不上。

一个非常隐蔽的情况是:IDEA 界面里“Build Project”成功,但 Artifacts 没有触发重新编译,IDEA 直接把旧的编译产物丢进了 jar。尤其是你只改了某个类的包名、没有 clean 的情况下,out 目录里可能残留两个包路径的 .class 文件,而 MANIFEST 指向新包名时,jar 里旧包结构还在但不是你要的入口类。

2.3 条件三:lib 依赖没进包,主类加载了但依赖缺失

第三种情况的表现是“能找到主类”,但紧接着报NoClassDefFoundError或ClassNotFoundException。这是因为你用了第三方库(比如 Fastjson、HttpClient 或 Spring 的某个模块),但打出来的 jar 是“裸 jar”,里面只有你自己的代码,没有把依赖的 .class 文件打进去。裸 jar 运行时,JVM 只会在 jar 包内部和系统 classpath 里找依赖,找不到就抛异常。

所以严格来说,“找不到或无法加载主类”在非精确报错时,也可能以NoClassDefFoundError的形式出现,因为你主类的字节码引用了某个缺失的类,JVM 在“加载主类”过程中触发依赖解析,失败后就报了主类无法加载。这就是为什么排查要先看报错是“找不到主类”还是“主类找到了但依赖缺失”——这两者的解决路径完全不同。

3. 用 IDEA 打包 jar:两种可靠方式与最小操作步骤

这里的场景分两种:纯手写的小项目(没有 Maven/Gradle 管理依赖),和用 Maven 管理的标准项目。先说结论:如果你用 Maven,不要用 IDEA 左侧的 Artifacts 打包,直接在 Maven 面板里package;如果你没有 Maven,用 Artifacts 方式且必须把依赖打进去才能java -jar运行。下面分别给操作步骤。

3.1 方式一:Artifacts + Build 菜单打包(适合无第三方依赖的项目)

适用于“没有 pom.xml、只依赖 JDK 自带类库”的小工具类项目。操作路径:

File -> Project Structure -> Artifacts -> 加号 -> JAR -> From modules with dependencies

在弹出的窗口里:

  1. Main Class 选择你的启动类(IDEA 会自动帮你填到 MANIFEST 里)
  2. JAR files from libraries 选择extract to the target JAR(把依赖解压并合入 jar)
  3. Directory for META-INF/MANIFEST.MF 选择src/main/resources(如果你有 resources 目录)

注意这里的关键点:如果你选择了copy to the output directory and link via manifest,那么依赖不会合入 jar,而是被打到 jar 包旁边的lib/目录,并且 MANIFEST 里的Class-Path会写成相对路径。没有 Maven 的情况下,这种 jar 在你自己机器上能跑(因为 lib 目录就在旁边),但拷给别人时如果把 lib 目录丢了就直接报主类找不到或 NoClassDefFoundError。所以无依赖项目我一般直接选extract to the target JAR。

3.2 方式二:Maven 插件打包(适合多模块和第三方依赖项目)

如果项目里有 pom.xml,就不要用 Artifacts 了。在 IDEA 右侧 Maven 面板里执行:

mvn clean package

这时 Maven 会按 pom.xml 里的配置编译、测试、打包。但package打出的 jar 默认是裸 jar,除非你在 pom 里配置了maven-assembly-plugin或maven-shade-plugin。区别在于:assembly是“把依赖解压后合并成一个 fat jar”,shade是“把依赖的类和资源重新定位后合并,并支持排除不需要的类”。对于“找不到主类”这个问题,两者都能解决主类定位和依赖缺失,但 shade 的稳定性和可配置性更好,尤其适合依赖较多、存在同名资源文件(比如META-INF/services/xxx)的项目。

一个最简的 shade 配置示例:

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.example.Main</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin>

这里mainClass写的就是你的入口类全限定名,shade 插件会把这个值写进最终 jar 的 MANIFEST.MF。如果你的项目有多个模块,比如一个common模块、一个app模块,记得只在app(包含 main 方法的模块)里配置 shade 插件,否则你会在common.jar里也生成一个 MANIFEST,且没有 Main-Class,运行时照样报错。

3.3 打包后先做“本地冒烟验证”:java -jar 之前先看包内结构

打完包不要急着跑,先看 jar 包里的文件结构是不是符合预期。用命令:

jar tf target/xxx.jar | head -50

或者直接解压看关键文件:

unzip -p target/xxx.jar META-INF/MANIFEST.MF

前一条命令会列出 jar 包内的所有路径,你至少要确认:com/example/Main.class存在(路径和 Main-Class 里写的包名一致)、META-INF/MANIFEST.MF存在。后一条命令直接打印 MANIFEST 内容,你会看到:

Manifest-Version: 1.0 Main-Class: com.example.Main

如果这两项都对,但java -jar还是报错,那就是依赖问题,接着用jar tf找依赖包的类是否在里面。我一般会在打包后先跑一次:

java -jar target/xxx.jar

看是否复现问题,再决定是调整 Artifacts 还是 Maven 配置。这一步虽然简单,但能帮你省掉后面环境变量的排查时间。

4. IDEA 打包后运行报错的 5 条踩坑与排查记录

这个报错的排查说难不难,但“代码能跑、打包就跑不起来”的场景重复率极高。下面按我处理过的真实情况把它们列出来,每条按“现象 → 原因 → 解决”来说。注意:以下案例和涉及项目均为模拟项目X、某图像处理Demo 等虚构代称。

4.1 报错“找不到或无法加载主类”但代码在 IDEA 里能跑

现象:IDEA 里右键运行Main.main()一切正常,但java -jar起来后立刻报“找不到或无法加载主类”。真实案例是某图像处理Demo:代码引用了 OpenCV 的 Java 包装类,IDEA 运行时会自动把依赖的 classpath 加进去,但打包时 Artifacts 配置的是“裸 jar”,依赖没进去。JVM 尝试加载Main类时,因为字节码里引用了 OpenCV 的类,加载失败,报“找不到或无法加载主类”。

原因:JVM 在主类加载阶段会解析常量池里的类引用,如果引用的类在 classpath 中缺失,整个主类加载就失败。在 IDEA 里运行和命令行运行的区别,就是 IDEA 自动拼了依赖的 classpath。

解决:改用 Maven shade 或 assembly 打 fat jar,把依赖合进 jar;或者手动把依赖目录加到运行命令里。如果你确实想让 jar 保持小体积,可以打“lib + jar”结构,但必须把整个目录一起交付,且确保 MANIFEST 的Class-Path指向 lib 下的每个 jar。

4.2 Manifest 文件里 Main-Class 末尾多了空格或回车

现象:某些 Windows 上手动编辑 MANIFEST.MF 或用旧版 IDEA 生成的 Artifacts,打包完成后java -jar报错格式为“错误: 找不到或无法加载主类 xxxxx”。用unzip -p看 MANIFEST 时,Main-Class: com.example.Main末尾带了个空格,或者可见\r字符。

原因:MANIFEST.MF 是文本文件,但 JVM 解析时对行的处理并不做 trim。末尾空格或 Windows 换行符\r会导致类名被拼接成一个带不可见字符的字符串。

解决:不手写 MANIFEST,让 IDEA 或 Maven 插件自动生成。如果你必须手动改,编辑后用xxd或od -c检查文件末尾,确保行结束是\n,且值后面没有额外空格。在 IDEA 里,检查 Artifacts 配置时不要自己去填Main-Class,而是通过“选择 Main Class”按钮让 IDEA 写入。

4.3 用 Maven 打包后 jar 包只有几 KB,运行时必然起不来

现象:执行mvn clean package后,target/目录下生成的 jar 只有 5KB 左右,里面只有自己写的几个类,没有第三方库的类。java -jar后报“找不到或无法加载主类”,或者主类找到了但立即报ClassNotFoundException。

原因:默认maven-jar-plugin只打包当前模块的源码编译产物,不会把依赖打进 jar。这是 Maven 的默认行为,不是 bug。只有当 pom 里配置了 shade 或 assembly 插件,依赖才会被合入。

解决:在需要打成可执行 fat jar 的模块的 pom.xml 里增加 shade 插件配置,或改用 assembly。注意maven-assembly-plugin和 shade 不能同时作用于同一个 package 阶段,否则会生成两个 jar(一个是裸 jar,一个是 fat jar),运行时要确保你执行的是 fat jar 的名字。常见做法是把裸 jar 设置成finalName不同后缀,或者直接删除裸 jar 的生成。

4.4 提示“NoClassDefFoundError”而不是“主类找不到”

现象:报错是Exception in thread "main" java.lang.NoClassDefFoundError: com/example/SomeUtils,但前面没有“错误: 找不到或无法加载主类”。这说明主类已经被找到并开始加载了,是在加载主类引用的另一个类时失败。

原因:这个被引用类不在 jar 里,且不在 classpath 中。常见于:项目用到了某个依赖,但 Artifacts 的JAR files from libraries选项选错了;或 Maven 打包时某个依赖被标记为<scope>provided</scope>,导致它没被打进 fat jar。另一个隐蔽原因是:某依赖的pom.xml里把传递依赖标成了optional,导致传递依赖没被引入。

解决:先看jar tf xxx.jar里有没有com/example/SomeUtils.class,没有就检修依赖。检查当前依赖的 scope 是不是compile(默认值),不要用provided和runtime(如果你的代码要引用它)。如果确认代码确实需要某个传递依赖,直接在当前模块的 pom.xml 里显式声明它,坐标和版本号对齐原依赖里的声明。

4.5 打出来的 jar 在命令行能跑,双击却不行

现象:在终端里执行java -jar xxx.jar运行正常,但双击 jar 文件时系统提示找不到主类,或报错“Failed to load Main-Class manifest attribute from xxx.jar”。

原因:双击 jar 文件时,Windows 的关联程序是按注册表里的javaw.exe来运行的,而javaw.exe不打开控制台,有些情况下还会因为持续集成环境里 PATH 没有java而失败。另一部分是文件关联的默认选项可能对应旧版 JDK 或其他打包产物。

解决:不要在交付时依赖双击行为。任何可执行 jar 的交付验证都以命令行java -jar为准。如果一定要双击,检查系统环境变量JAVA_HOME和PATH指向的是不是当前项目编译用的 JDK 版本。如果开发机和交付机 JDK 版本不同(比如项目用 JDK 17 特性编译,对方机器是 JDK 8),启动会失败,报错内容也可能回溯成“找不到主类”。

5. 从“能打包”到“能交付”:三个必查的验证项与参数设置

这个阶段不是“跑通一次”就完了,而是让你形成一套固定的验证流程,保证每次打出来的 jar 都是可交付状态。

5.1 用 java -jar 跑通三个前置检查

打包完成后,按顺序检查三项:

  1. 检查 manifest 内容:unzip -p xxx.jar META-INF/MANIFEST.MF
  2. 检查主类对应的 class 文件是否存在:jar tf xxx.jar | grep "Main.class"
  3. 检查依赖是否齐全:依赖类的典型路径,或直接统计 jar 包里的 class 文件总数,如果只有几个而你引入了大量第三方库,必然有问题。

对第 2 项,如果多个模块里有多个同名 Main 类,注意看完整路径是哪一个。对第 3 项,粗看数字意义不大,关键是“主类字节码里引用的所有类”都在包里。想确认全部依赖,可以不严谨但有效地定位“报错里缺哪个类,就找哪个类;不报错但怀疑缺,就用jdeps分析字节码依赖,输出里看哪些类不在 JDK 里,再去迭代检查”。

可以写一个小测试命令来直接验证主类加载:

java -cp xxx.jar com.example.Main

这一步和java -jar的区别在于:-jar模式强制读 MANIFEST,忽略-cp;用-cp指定类名时只查 classpath。如果你的-cp xxx.jar com.example.Main能跑,但java -jar xxx.jar报主类找不到,说明 MANIFEST 里的Main-Class写错了;如果两边都报错,先确认类路径里有没有你的 jar。

5.2 Build 菜单里的 “Build Artifacts” 和 “Rebuild” 区别

IDEA 里点击Build -> Build Artifacts时,有Build和Rebuild两个动作,它们之间有细微差别。Build是增量构建,比 IDEA 在构建前会检查是否有模型或源码更新,如果没有则直接复用上一步的产物,把 jar 重新组装;Rebuild是清掉所有已存的编译产物和 jar 包,从头编译一遍再组装。

对于“找不到主类”这类问题,如果你改过 Main 类包路径、改过 MANIFEST 相关配置,我建议直接用Rebuild。原因很简单:增量构建在某些版本里不会重新生成 MANIFEST.MF,旧的 manifest 残留着旧类名,而你在 Project Structure 里改的配置没被同步进 manifest。用Rebuild能减少这类残留问题。如果你用了 Maven,那直接在 Maven 里执行clean命令再做package,等价于 Rebuild。

5.3 交付前检查清单:JDK 版本、控制台输出、退出码

交付 jar 给他人或部署前,我一般再过以下几点:

检查项说明失败时的表现
编译 JDK 版本项目编译 target 指定了几,交付机至少是同版本或更高主类找不到或UnsupportedClassVersionError
fat jar 还是裸 jar用jar tf看是否有依赖类的路径运行时 NoClassDefFoundError
MANIFEST 中 Main-Class 写法全限定名,不带.class,前不加-cp或空格主类找不到
启动命令有无依赖外部目录如果Class-Path写了lib/xxx.jar,交付时必须带 lib 目录主类找不到或依赖缺失
日志/控制台输出代码里的System.out不会自动出现在 javaw 窗口双击时看起来“没反应”

其中“编译 JDK 版本”这一点容易踩。如果你的 pom.xml 里没有明确<maven.compiler.source>和<maven.compiler.target>,有时 IDEA 里默认使用当前 JDK 编译,生成 class 版本号对应当前 JDK。到了只有 JDK 8 的机器上,就会报UnsupportedClassVersionError。这不是“主类”问题,但用户感知上也是“起不来”。

6. 把“一键打包”做成肌肉记忆:自定义运行配置与后续排错习惯

如果你已经能打出可运行的 fat jar,事后想要提升效率,有两个方向可以走:给 IDEA 配置一个一键打包的运行配置,以及在 Windows/macOS 上做一个小组脚本封装。

第一步,在 IDEA 里加自定义配置。路径是Run/Debug Configurations -> 加号 -> Maven,然后设置:

  • Name:package-fat-jar
  • Working directory:你的项目根目录
  • Command line:clean package

这样你以后点一下绿色箭头就能执行完整打包,不需要每次去右侧 Maven 面板里找命令。如果你有多个模块,注意命令行里加-pl <模块名> -am来精确指定只构建某模块及其依赖模块。

第二步,写一个简单的运行脚本,解决交付场景里java -jar命令太长、参数忘带的问题。脚本内容可以是:

#!/usr/bin/env bash JAR_PATH="target/xxx.jar" JAVA_OPTS="-Xms128m -Xmx512m" exec java ${JAVA_OPTS} -jar ${JAR_PATH} "$@"

这里"$@"用于透传外部参数给主类的main方法,JAVA_OPTS可以写内存参数或系统属性,显式放在-jar之前,避免把 JVM 参数误传给应用本身。脚本在 Windows 上对应写一个.bat文件即可,原理一样。

第三步,是这个报错的长期复盘习惯。如果你再次遇到“找不到或无法加载主类”,不要急着重打 jar,一定先看 MANIFEST 内容和包内结构,这两个信息能覆盖八成以上问题。剩下的两成,多出现在Class-Path写的是相对路径,你把 jar 挪了位置,或者用maven-assembly-plugin生成了多个同名 jar 而你执行错了那个。

我做这类打包问题的排错时有个习惯:在项目根目录下放一个verify.sh,内容就是前面说的三条前置检查——打印 MANIFEST、列出主类是否存在、统计包内 class 数。打包后跑一遍这个脚本,比反复按 Enter 重跑java -jar要快得多,也能让交接给别人的时候少一点“我这边跑得好好的啊”这种来回拉扯。希望这一套流程对你有用,下次再碰到 jar 起不来,能先自己把它钉死在某一环,再动手解决。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询