JDK17下javac与java命令执行差异的原因咨询
问题背景
我使用JDK 17,代码如下:
package myPackage; public class myClass { public static void main(String[] args){ System.out.println("test"); } }
执行场景1:报错
在IntelliJ终端执行命令:
javac MyClass.java java MyClass
出现错误:
Error: Could not find or load main class MyClass
java.lang.NoClassDefFoundError: myPackage/myClass(wrong name: MyClass)
执行场景2:成功运行
在IntelliJ终端执行命令:
javac MyClass.java java MyClass.java
程序正常输出,无报错。
补充:当前终端工作目录就是存放MyClass.java的目录,文件内明确声明了myPackage包。
原因说明
1. 第一种执行方式报错的核心原因
你的代码声明了myPackage包,Java规范要求:带包名的类,编译后的.class文件必须放在与包名对应的目录结构下(也就是要放在myPackage/子目录里),而且运行时必须从包结构的根目录启动,同时指定完整的类名(包名.类名)。
你直接在当前目录执行javac MyClass.java,编译出的MyClass.class会留在当前目录,没有自动生成myPackage子目录来存放它。这时候执行java MyClass,JVM会去找没有包名的MyClass类,但实际编译出来的类是属于myPackage的(完整类名是myPackage.myClass),自然找不到,所以报错提示类名不匹配。
正确的执行步骤应该是这样:
- 先创建
myPackage目录,把MyClass.java移进去 - 回到
myPackage的父目录,执行javac myPackage/MyClass.java - 再执行
java myPackage.myClass
或者用javac的-d参数指定输出目录,自动生成包结构:
javac -d . MyClass.java java myPackage.myClass
2. 第二种执行方式成功的原因
从JDK 11开始,Java支持直接运行源文件的特性(java <源文件名>.java)。这种模式下,JVM会自动完成编译、临时生成符合包结构的类文件、加载运行的全流程,运行完还会自动清理临时文件,不需要你手动维护目录结构或者输入完整类名,所以能直接正常运行。
内容的提问来源于stack exchange,提问作者Vats

