“ Java”命令是否编译 Java 程序?

互联网上的大多数网站都表示:

“使用 javac命令编译一个 .java文件。然后使用 java命令运行它”

但是今天我尝试运行一个没有 javac的 java 程序,我得到了一个奇怪的结果。

下面是一个名为 hello.java的文件的内容:

public class Myclass {
public static void main(String[] args){
System.out.println("hello world");
}
}

然后我跑过去:

$ javac hello.java

这就给了我一个错误:

hello.java:1: error: class Myclass is public, should be declared in a file named Myclass.java
public class Myclass {
^
1 error

但是当我不使用 javac命令运行它时,它的执行没有任何错误。

$ java hello.java
hello world

java命令是否也编译程序? 如果是,为什么我们需要 javac命令?

我的 java 版本是:

openjdk version "12.0.2" 2019-07-16
OpenJDK Runtime Environment (build 12.0.2+10)
OpenJDK 64-Bit Server VM (build 12.0.2+10, mixed mode)
12112 次浏览

如果您正在运行 Java11, 有一个允许单源文件执行的新特性。单一源编译器在类名和文件名方面更加混杂,因此这就是您能够运行但不能成功编译的原因。

如果您使用的是以前的 Java 版本, ,那么由于编译错误,当前的 hello.Java 无法编译,特别是围绕类名。所以调用 java hello.java 绝对不可能编译你的代码,因为它不会编译。

看起来最有可能的情况是,您在执行 java 命令时正在运行一些以前编译过的代码。

在 Java11之前,要运行代码,必须先编译它,然后才能运行它。这里有一个例子:

javac test.java
java test

从 Java11开始,您仍然可以执行 javac + java,或者您可以单独运行 java来编译和自动运行您的代码。注意,不会生成 .class文件。这里有一个例子:

java test.java

如果运行 java -help,您将看到各种允许的用法。这是我机器上的样子。最后一个是您遇到的: java [options] <sourcefile> [args],它将“执行一个源文件程序”。

$ java -help
Usage: java [options] <mainclass> [args...]
(to execute a class)
or  java [options] -jar <jarfile> [args...]
(to execute a jar file)
or  java [options] -m <module>[/<mainclass>] [args...]
java [options] --module <module>[/<mainclass>] [args...]
(to execute the main class in a module)
or  java [options] <sourcefile> [args]
(to execute a single source-file program)

更新:

正如@BillK 所指出的,OP 还问道:

为什么我们需要 javac 命令?

我们需要 javac的原因是创建 .class文件,这样代码就可以像今天一样被创建、测试、分发、运行、共享等等。JEP 330的动机是在不改变任何其他现有用途的情况下简化 “学习 Java 的早期阶段,以及编写小型实用程序时”

是的,但不是你想的那种方式。

使用 javac命令编译。将 java 文件转换为。类文件的输出称为字节码。字节码是基于 Java 虚拟机规范的理论 CPU 的机器代码(本机指令)。

这个虚拟 CPU 规范是在编写规范时通用的 CPU 类型的平均值。因为它接近于许多不同类型的 CPU,所以更容易运行同一个 Java。多 CPU 类型的类文件。

当 Java 首次启动时,java命令将读取。类文件,并一次解释一个字节码指令,然后将它们映射到等效的本机指令,不管它实际上运行在哪个 CPU 上。这种方法奏效了,但并不是特别快。为了改进这种即时(JIT)编译被添加到 JavaRuntime 中。

使用 JIT,java命令获取字节码并再次将其编译为运行在其上的 CPU 的本机指令。现代 Java 运行时倾向于在后台进行 JIT 编译时开始解释字节码,并在准备好后切换到编译的本机指令,同时还会分析运行的应用程序,然后再次用不同的优化重新编译字节码,以获得最佳性能。

编辑(为了安抚那些情绪低落的选民) :

因此,在您的特定情况下(当您运行比 v11更新的 JRE 时) ,代码将被编译(至少)两次

  1. 作为字节码的一个.java 文件
  2. 通过 JIT 编译器解释字节码(尽管对于 helloWorld,它可能实际上没有时间运行任何已编译的本机代码)

要回答为什么给出这个错误,文件的类名必须与文件的 basename匹配。

你有两个选择让这个代码工作在传统的 javac; java序列:

  1. 将类重命名为 public class Hello

  2. hello.java重命名为 myclass.java

Java11的 java解释器没有强制执行这个要求。包含 main的类可以有任何名称,只要它是文件中的第一个类。这主要是为了简化初学者的学习过程,并允许使用 shebang (裁判。)编写“ java 脚本”。