先说结论:
你写 .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 做三件事,直接把这些问题按住:

  1. 依赖管理:你在一个文件里写“我要 A 包 1.2 版”,它自动下载、放好位置;

  2. 标准结构:大家都按同一套目录来放代码,协作少踩坑;

  3. 统一流程:一条命令从编译、测试到打包,适合 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 配好,说明你已经会开店了:备料、流程、出品、打包外卖,全都在一个系统里。
等你下次点“运行”按钮时,背后那套流程,其实已经在替你忙活了。

更多推荐