Java源代码通过javac编译为.class字节码文件,该过程不生成机器码、无需链接,故跨平台;需确保类名与文件名严格一致,依赖文件需手动指定或用-sourcepath,输出目录用-d控制。

Java源代码怎么变成.class文件
Java程序的编译由javac完成,它把.java文件翻译成JVM能识别的字节码(.class文件),不是直接转为机器码。这个过程不涉及链接或平台适配,所以编译结果是跨平台的。
常见错误:javac HelloWorld.java报错“找不到符号”,大概率是类名和文件名不一致——public class HelloWorld必须保存在HelloWorld.java中,大小写也必须完全匹配。
-
javac默认只编译指定文件,不会自动编译其依赖的其他.java文件;有依赖时得手动列全,或用-sourcepath指定源码路径 - 编译输出目录可用
-d控制,例如javac -d ./out src/HelloWorld.java,否则.class会生成在当前目录,容易混乱 - Java 14+ 默认启用
--enable-preview特性需显式加参数,否则新语法(如record)会编译失败
JVM如何加载并运行.class文件
执行阶段由java命令触发,它启动JVM,再由类加载器(ClassLoader)按需读取.class字节码,经过验证、准备、解析、初始化后进入运行时数据区。注意:这里不重新编译,也不检查源码是否存在。
典型问题:java HelloWorld报错Exception in thread "main" java.lang.NoClassDefFoundError: HelloWorld,往往不是没编译,而是类路径不对——JVM找不到HelloWorld.class,尤其当它在子目录或-d指定了输出目录时。
立即学习“Java免费学习笔记(深入)”;
这本书给出了一份关于python这门优美语言的精要的参考。作者通过一个完整而清晰的入门指引将你带入python的乐园,随后在语法、类型和对象、运算符与表达式、控制流函数与函数编程、类及面向对象编程、模块和包、输入输出、执行环境等多方面给出了详尽的讲解。如果你想加入 python的世界,David M beazley的这本书可不要错过哦。 (封面是最新英文版的,中文版貌似只译到第二版)
- 运行时必须用类名(不含
.class后缀),且区分包结构;例如com.example.Hello要放在com/example/Hello.class路径下,并用java -cp . com.example.Hello -
java默认只查当前目录和CLASSPATH环境变量,不自动包含./out或src等目录 - 如果类依赖第三方jar,必须用
-cp或--class-path显式引入,例如java -cp ".:lib/gson.jar" Main(Linux/macOS)或java -cp ".;lib/gson.jar" Main(Windows)
从字节码到机器指令发生了什么
JVM不直接执行字节码,而是通过解释器逐行翻译,或在热点代码处用JIT编译器(如HotSpot的C1/C2)编译为本地机器码。这个过程对开发者透明,但影响实际性能表现。
容易被忽略的一点:JIT优化基于运行时行为,所以同一段代码在不同负载、不同JVM参数下,执行效率可能差异很大。比如-XX:+TieredStopAtLevel=1会禁用C2编译,强制只用C1,适合调试但性能下降明显。
- 可通过
-XX:+PrintCompilation看到哪些方法被JIT编译了 -
-Xcomp强制所有方法首次执行就编译(跳过解释阶段),但启动更慢,且未必提升整体吞吐 - Java 9+ 的
jshell是独立于标准JVM流程的REPL环境,它内部仍走编译→字节码→JVM加载链路,只是隐藏了文件落地步骤
为什么修改.java后不重新运行就看不到效果
因为java命令只读.class,不关联源码。改完.java必须重新javac,否则运行的还是旧字节码。IDE通常自动做这一步,但命令行里极易遗漏。
另一个陷阱:有些构建工具(如Maven)默认使用target/classes里的字节码,而你手动javac生成在当前目录,导致mvn exec:java运行的是旧版本。
- 确认正在运行的字节码来源:用
java -verbose:class HelloWorld 2>&1 | grep HelloWorld看JVM实际加载的是哪个路径下的类 - 清理残留
.class最稳妥的方式是删掉整个输出目录(如rm -rf out/或del /s/q out\),而不是只删某个文件 - Java 11+ 的
java HelloWorld.java(直接运行源文件)是特殊语法糖,底层仍会隐式调用javac并缓存字节码到临时目录,但仅限单文件且无依赖场景









