javac/java:从源码到运行
更新时间:2026-09-20。JDK 工具链第一篇。
javac负责编译期,java负责运行期,理解这条边界,后面的 jps/jcmd/jstack、classpath 冲突、UnsupportedClassVersionError都好理解。环境安装见 Java 基础入门。
一、一条链路:.java → .class → JVM
# 1) 编译:javac 读源码,产出 JVM 可执行的字节码 .class
javac Hello.java # 生成 Hello.class
# 2) 运行:java 启动 JVM,找含 main 的主类(写全限定类名,不带 .class)
java Hello| 概念 | 是什么 | 关键点 |
|---|---|---|
.java | 源文件 | 一个文件最多一个 public 类,且文件名与 public 类同名 |
.class | 字节码 | 与平台无关,靠各平台 JVM 解释/JIT 执行 |
| JDK | 开发工具包 | 含 javac、jar、jcmd 等 + JRE |
| JRE | 运行环境 | 只有 java,不能编译;JDK 11 起不再单独发 JRE |
有包名时:
package com.demo;的类必须放在com/demo/目录,运行也要写全限定名。
mkdir -p out
javac -d out src/com/demo/Hello.java # -d 按包结构把 .class 输出到 out/
java -cp out com.demo.Hello # -cp 指定类从哪找;主类写全限定名二、classpath(-cp / -classpath)
-cp 告诉 JVM/编译器去哪些目录或 jar 里找类:
# 多个路径用分隔符:Linux/macOS 用冒号 :,Windows 用分号 ;
java -cp "out:lib/mysql.jar:lib/*" com.demo.Main # lib/* 表示目录下所有 jar
javac -cp "lib/*" -d out $(find src -name '*.java') # 一次编译整个源码树- 不写
-cp时默认是当前目录.,所以在 class 所在目录直接java Hello能跑,换目录就报找不到主类。 lib/*只匹配该目录下的 jar/zip,不递归子目录,也不匹配.class。
三、常用选项
| 选项 | 作用 |
|---|---|
-d <dir> | 指定字节码输出目录(自动建包路径) |
-cp/-classpath | 编译/运行时类搜索路径 |
-source/-target(旧)或 --release | 限定源码与字节码版本,--release 17 最省心 |
-encoding UTF-8 | Windows 下中文源码不加这个常乱码 |
-verbose / -Xlint | 看编译过程细节 / 打开全部警告 |
-XshowSettings:properties | 打印 JVM 实际生效的属性,排查环境问题 |
四、两个高频坑
1. 找不到或无法加载主类(ClassNotFoundException / 找不到主类)
- 带
.class后缀去java Hello.class—— 错,主类不要后缀; - 有包名却写简单类名,或
-cp没指到 class 根目录; - 类在 jar 里但没把该 jar 加进
-cp,或 jar 的META-INF/MANIFEST.MF没配Main-Class却用java -jar。
2. UnsupportedClassVersionError(版本不匹配)
高版本 JDK 编译出的 class 在低版本 JRE 上跑就会报,class file 版本号对不上。要么升级运行 JDK,要么用 javac --release 8(或对应版本)向下兼容;java -version 与编译用的 JDK 要确认是同一个。
五、和构建工具的关系
真实工程不会手敲一长串 javac,交给 Maven/Gradle 管理依赖与 classpath,但底层仍是 javac/java:看懂「编译期 vs 运行期 classpath」才能解决 NoClassDefFoundError(编译期有、运行期缺)这类问题。诊断运行中的进程则用 jps、jcmd。
总结
javac 把 .java 编译成与平台无关的 .class,java 启动 JVM 加载主类运行;有包名必须「目录结构=包结构、主类写全限定名、用 -cp 指向 class 根目录与依赖 jar」,多路径分隔符 Linux 用 :、Windows 用 ;;最常见两类报错是 classpath 没配对导致找不到主类,以及高版本编译低版本运行导致的 UnsupportedClassVersionError;工程里用 Maven/Gradle 托管,但编译期/运行期 classpath 的区别仍是排查依赖缺失的根基。