【问题标题】:Does the 'java' command compile Java programs?'java' 命令是否编译 Java 程序?
【发布时间】:2020-01-24 22:31:49
【问题描述】:

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

“使用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)

【问题讨论】:

  • 您使用的是哪个版本?我认为他们在 Java 9 中引入了 Java Console,这可能就是您所经历的。
  • 您需要将类名与其文件名相匹配——这是 Java 标准。只需将文件名更改为Myclass.java,然后从命令行编译它像这样javac Myclass.java,然后像这样运行它java Myclass
  • 是的,javac 仍然用于编译,如果您不想部署源代码,或者您有多个文件(documentationjava 用于源文件选项:仅用于启动单个源文件程序。)
  • @Matthieu "java -version" 的输出是: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,混合模式)
  • @Milad - 这是怎么回事 - javac 将 Java 源代码编译为 JVM 特定的解释字节码,java 命令将其加载到 JVM 的 ClassLoader 中。

标签: java javac


【解决方案1】:

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

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

在执行 java 命令时,您似乎很可能正在运行一些以前编译过的代码。

【讨论】:

  • 谢谢,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、混合模式)
  • check Using Source-File Mode to Launch Single-File Source-Code Programs: "编译器不会强制执行 JLS ??7.6 末尾定义的可选限制,即命名包中的类型应该存在于名称由类型名称后跟 .java 扩展名。”
  • Java 脚本 API 和 Java 单文件源代码程序启动 (JEP 330) 是两个完全独立且完全不相关的东西。
  • @DavidConrad,相应地更新了措辞。谢谢。
  • 欣赏输入,@T.J.Crowder。但是,我很确定我是按原样写的。此外,链接中的第二个定义:混杂意味着包括各种不同的事物。
【解决方案2】:

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

javac test.java
java test

从 Java 11 开始,您仍然可以使用 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 的早期阶段以及编写小型实用程序时”更容易,而不会改变任何其他现有用途。

【讨论】:

  • 感谢@CarlosHeuberger 提供更多详细信息。我在回答中做了一个小修改,以反映它是在 Java 11 中引入的。
  • @Spikatrix 那是 Java 8(在较新的版本中,他们在 1.8 中删除了 1.
  • 你没有回答为什么我们仍然需要 javac 的问题——我认为 java 只对你提供的单个文件和以前编译的文件进行操作。我相信您必须从您调用的文件中编译您希望使用的所有其他文件。
  • 这个答案没有解决为什么这个新方法不会导致javac报告的文件名与类名错误。
【解决方案3】:

是的,但不是你说的那样。

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

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

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

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

编辑(安抚反对者):

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

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

【讨论】:

  • 这不能回答问题。
  • @DavidConrad 但确实如此! “'java' 命令是否编译 Java 程序?”的答案是一个响亮的“是”,因为 hardlib 在这里给出的原因:它将及时将字节码编译为本机指令(对于非平凡的程序,具有标准设置)。
  • 现在是强制编译吗?从历史上看,Java 字节码可以被解释; JIT 编译是可选的。
  • 这些天(很长一段时间)默认情况下 JIT 处于打开状态,如版本输出中的 mixed-mode 所示
【解决方案4】:

要回答为什么会出现此错误,文件的类名必须与文件的 basename 匹配。

您有两种选择让此代码适用于传统的javacjava序列:

  1. 将类重命名为 public class Hello

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

Java 11 的 java 解释器没有强加此要求。包含main 的类可以有任何名称,只要它是文件中的第一个类即可。这主要是为了简化初学者的学习过程,并允许使用 shebang (ref.) 进行“java 脚本”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-26
    • 1970-01-01
    • 1970-01-01
    • 2015-12-31
    • 1970-01-01
    • 2015-02-21
    • 1970-01-01
    相关资源
    最近更新 更多