“ java”命令可以编译Java程序吗?

mil*_*lad 143 java javac

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

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

但是今天我试图在没有Java的情况下运行Java程序,但javac结果却很奇怪。

以下是名为的文件的内容hello.java

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

然后我跑了:

$ javac hello.java
Run Code Online (Sandbox Code Playgroud)

这给了我这个错误:

hello.java:1: error: class Myclass is public, should be declared in a file named Myclass.java
public class Myclass {
       ^
1 error
Run Code Online (Sandbox Code Playgroud)

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

$ java hello.java
hello world
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

kaa*_*aan 185

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

javac test.java
java test
Run Code Online (Sandbox Code Playgroud)

从Java 11开始,您仍然可以执行javac+ java,也可以单独运行java以编译和自动运行代码。请注意,.class将不会生成任何文件。这是一个例子:

java test.java
Run Code Online (Sandbox Code Playgroud)

如果运行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)
Run Code Online (Sandbox Code Playgroud)

更新:

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

为什么我们需要javac命令?

我们需要的原因javac是创建.class文件,以便可以像现在一样创建,测试,分发,运行,共享代码等。JEP 330的动机是使“学习Java的早期阶段以及编写小型实用程序时”变得更容易,而无需更改任何其他现有用法。

  • Java 11中引入的内容:[新增功能](https://www.oracle.com/technetwork/java/javase/11-relnote-issues-5012449.html#NewFeature)或/和[JEP 330:启动单文件源代码程序](http://openjdk.java.net/jeps/330) (49认同)
  • @Spikatrix是Java 8(在较新的发行版中,他们在1.8中删除了1.。) (7认同)
  • 您没有回答为什么我们仍然需要 javac 的问题——我认为 java 只对您提供的单个文件和以前编译的文件进行操作。我相信您必须从您调用的文件中编译您希望使用的所有其他文件。 (2认同)
  • 这个答案不能解决为什么这个新方法不会导致`javac`报告的文件名和类名错误。 (2认同)

Eva*_*van 52

如果您运行的是Java 11,则有一项新功能允许单个源文件执行。在类名与文件名方面,单源编译器更加繁琐,因此您可以运行但无法成功编译。

如果您使用的是Java的早期版本,则由于编译错误(尤其是围绕类名的问题)您当前的hello.java无法编译。因此,绝对不可能调用java hello.java来编译您的代码,因为它不会编译。

似乎最有可能在执行java命令时运行了一些以前编译的代码。

  • 选中[使用源文件模式启动单文件源代码程序](https://docs.oracle.com/zh_cn/java/javase/13/docs/specs/man/java.html#using-source-文件模式启动单个文件源代码程序):“编译器不强制执行JLS 7.6末尾定义的可选限制,即文件中应存在命名包中的类型其名称由类型名称后跟.java扩展名组成。” (5认同)
  • Java脚本API和Java单文件源代码程序启动([JEP 330](https://openjdk.java.net/jeps/330))是两个完全独立且完全不相关的东西。 (2认同)

JL2*_*210 6

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

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

  1. 将课程重命名为public class Hello

  2. 重命名hello.javamyclass.java

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


har*_*llb 5

是的,但不是您可能要说的那样。

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

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

首次启动Java时,该java命令将读取.class文件并一次解释一个字节码指令,然后将其映射到等效的本机指令,以了解其实际运行的CPU。这行得通,但并不是特别快。为了改善这一点,在Java运行时中添加了即时(JIT)编译。

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

编辑(安抚失败的选民):

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

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

  • 这不能回答问题。 (7认同)
  • @DavidConrad但是可以!答案为“'java'命令是否编译Java程序?” 由于hardlib在这里给出的原因,它是一个响亮的“是”:它将实时地将字节码编译为本地指令(对于具有标准设置的非平凡程序)。 (2认同)