'java' 命令是否编译 Java 程序?

Does the 'java' command compile Java programs?

互联网上的大多数网站都说:

"use the javac command to compile a .java file. Then run it using the java command"

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

以下是名为 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)

如果您是 运行ning Java 11, 有一项允许执行单个源文件的新功能。单源编译器在 class 名称与文件名方面更加混杂,因此这就是您能够 运行 但无法成功编译的原因。

如果你使用的是之前版本的Java,那么你当前的hello.java无法编译,因为编译错误,具体围绕class名字。所以调用 java hello.java 绝对不可能编译你的代码,因为它不编译。

很可能您在执行 java 命令时 运行ning 了一些以前编译的代码。

在Java 11之前,要运行你的代码你必须先编译它,然后你才能运行它。这是一个例子:

javac test.java
java test

从Java 11开始,你仍然可以做javac + java,或者你可以运行 java自己编译和自动运行 你的代码。请注意,不会生成 .class 文件。这是一个例子:

java test.java

如果您 运行 java -help,您将看到各种允许的用法。这是它在我的机器上的样子。最后一个是你 运行 变成的: java [options] <sourcefile> [args] 它将 "execute a single source-file program".

$ 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 还询问了:

why do we need the javac command?

我们需要 javac 的原因是创建 .class 文件,以便可以像今天一样创建、测试、分发、运行、共享等代码。 JEP 330 的动机是在不改变任何其他现有用途的情况下使 "early stages of learning Java, and when writing small utility programs" 更容易。

是的,但不是您想要的那样。

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

这个虚拟 CPU 规范是编写规范时常见的 CPU 类型的平均值。因此,它接近许多不同类型的 CPU,从而更容易 运行 相同的 Java .class 多个 CPU 类型的文件。

当 Java 首次启动时,java 命令将读取 .class 文件并一次解释一个字节码指令,然后将它们映射到等效的本机指令CPU 它实际上 运行 是什么。这有效但不是特别快。为了改进这种即时 (JIT) 编译,已将其添加到 Java 运行时。

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

编辑(安抚反对者):

因此在您的特定情况下(因为您运行宁一个比 v11 更新的 JRE)代码被编译(至少)两次

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

要回答出现此错误的原因,文件的 class 名称必须与文件的 basename.

匹配

您有两种选择可以使此代码适用于传统 javacjava 序列:

  1. 将 class 重命名为 public class Hello

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

Java 11 的 java 解释器没有强加这个要求。包含 main 的 class 可以有任何名称,只要它是文件中的第一个 class 即可。这主要是为了简化初学者的学习过程,并允许 "java scripting" 与 shebang (ref.)。