现代 Java 项目是如何运行的?从 javac 到 Maven 全流程揭秘
先说结论:
你写.java→ 用javac变成.class(通用“半成品”)→ 交给 JVM 跑起来。
真到工程里,几乎没人手敲javac,都用 Maven 管依赖、管流程、管打包。
0. 小场景
第一次写 Java 时,我以为敲完这段就结束了:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, Java!");
}
}
结果发现:能跑 和 一直稳稳地跑,是两件事。前者靠命令,后者靠流程(也就是 Maven)。
1. javac 在干嘛:把“人话”翻译成“通用配菜”
-
.java是人写给人的。 -
javac把它编成.class:这不是具体某台电脑的机器码,而是 JVM 都能看懂的“字节码”。
随手试一下(知道意思就行):
javac HelloWorld.java # 生成 HelloWorld.class
java HelloWorld # 运行
为什么说 Java 跨平台?
因为你带着同一个 .class,无论去 Windows、macOS 还是 Linux,只要有 JVM,就能跑。
2. JVM 在干嘛:把“配菜”做成熟菜
-
JVM(Java Virtual Machine)读取
.class,一边解释,一边把热点代码“现炒”成本地机器码(这就是 JIT,即时编译)。 -
你可以把它理解成:菜谱统一,但每家厨房现做,既通用,又能针对本地灶台优化。
3. 为啥还需要 Maven:从“能跑一次”到“到处都能跑”
小项目手敲命令没问题;但一旦项目有点规模,你会遇到:
-
依赖一大堆,自己下 jar、自己摆 classpath,容易乱;
-
要写测试、要打包、要发布,不同同事环境不一样;
-
线上线下“版本不一致综合征”。
Maven 做三件事,直接把这些问题按住:
-
依赖管理:你在一个文件里写“我要 A 包 1.2 版”,它自动下载、放好位置;
-
标准结构:大家都按同一套目录来放代码,协作少踩坑;
-
统一流程:一条命令从编译、测试到打包,适合 CI/CD。
4. Maven 项目长什么样?
my-app/
├─ pom.xml # “总管家”的清单
└─ src/
├─ main/java/ # 业务代码
└─ test/java/ # 测试代码
最小可用 pom.xml(Java 17 例子),能跑就够:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 示例依赖:需要就加,不需要就删 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 运行主类:开发期省心 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<mainClass>com.example.HelloWorld</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
提醒:包名和目录要对齐,比如
package com.example;→ 放到src/main/java/com/example/。
5. 真正的一次跑通(3 行命令)
mvn compile # 编译到 target/classes
mvn test # 跑测试(没有也会很快过)
mvn exec:java # 运行主类(在 pom.xml 里已经写了 mainClass)
需要打包给别人用?
mvn package
java -jar target/my-app-1.0.0.jar # 若想“开箱即跑”,见下一节
6. “可运行 JAR”怎么来?
-
普通 JAR 只打进你的代码,不含三方依赖;运行时还得配 classpath。
-
想“一包拿走就能跑”,做 Fat JAR(也叫 Uber JAR):把依赖也打进去。常见做法是用 shade 插件(生产上注意体积和冲突,简单小工具很方便)。
一句话:开发期用 mvn exec:java 最省心;要分发就打包,简单项目做个 Fat JAR 就行。
7. 常见小坑(别问我怎么知道的)
-
包名/目录不匹配 →
NoClassDefFoundError:检查package和目录层级。 -
JDK 版本不一致:编译用 17,运行机只有 11,会报错;
<maven.compiler.release>统一起来。 -
中文乱码:
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>放上。 -
只在测试用的依赖放成了运行依赖:
scope分清楚,JUnit 这类应该是test。 -
把所有东西都手工塞进 classpath:工程时代交给 Maven,少折腾自己。
8. 手工 vs Maven(各取所长)
-
学原理:
javac/java能让你看清“源码→字节码→JVM”的路径。 -
做工程:Maven 把“能跑一次”升级为“每次都能跑、在哪都能跑、团队一起跑”。
9. 一张“脑内流程图”(文字版)
.java --javac--> .class --JVM(解释+JIT)--> 本地机器码 → 输出
\
\(在工程里,这条线被 Maven 包成了“流程”:
管依赖、定结构、接测试、做打包、接入 CI)
10. 收个尾
会写 javac/java,说明你会做菜;
把 Maven 配好,说明你已经会开店了:备料、流程、出品、打包外卖,全都在一个系统里。
等你下次点“运行”按钮时,背后那套流程,其实已经在替你忙活了。
更多推荐


所有评论(0)