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


145

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

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

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

以下是名为的文件的内容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)

11
您正在使用哪个版本?我认为他们在Java 9中引入了Java控制台,这可能就是您所经历的。
Matthieu

6
您需要将类名与其文件名进行匹配-这是Java标准。只需将文件名更改为Myclass.java,然后从命令行像这样编译它javac Myclass.java,然后像这样运行它即可java Myclass
unnsse

6
是的,javac还是用来编译,如果你不希望部署的源代码,或者你有一个以上的单个文件(文档java:源文件选项仅用于启动一个源文件的程序。)
user85421

@Matthieu“ java -version”的输出是:openjdk版本“ 12.0.2” 2019-07-16 OpenJDK运行时环境(内部版本12.0.2 + 10)OpenJDK 64位服务器VM(内部版本12.0.2 + 10,混合模式)
milad

1
@Milad-这会发生什么- javac将Java源代码编译为JVM特定的解释字节码,然后该java命令将其加载到JVM的ClassLoader中。
unnsse

Answers:


188

在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中引入的
。– kaan

7
@Spikatrix这是Java的8(他们放弃了1.1.8中更新的版本)
穆鲁

1
您没有回答为什么我们仍然需要javac的问题-我认为Java仅在提供它的单个文件和以前编译的文件上运行。我相信您必须从调用的文件中编译希望使用的所有其他文件。
比尔K

2
这个答案不能解决为什么这种新方法不会导致文件名与类名错误的错误javac
sebrockm19年

52

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

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

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


谢谢,java版本是:openjdk版本“ 12.0.2” 2019-07-16 OpenJDK运行时环境(内部版本12.0.2 + 10)OpenJDK 64位服务器VM(内部版本12.0.2 + 10,混合模式)
milad

5
检查“ 使用源文件模式启动单文件源代码程序”:“编译器没有实施在JLS 7.6末尾定义的可选限制,即名称包中的类型应该存在于名称为的文件中由类型名称和.java扩展名组成。”
user85421 '19

2
Java脚本API和Java单文件源代码程序启动(JEP 330)是两个完全独立且完全不相关的事物。
戴维·康拉德

@DavidConrad,相应地更新了语词。谢谢。
埃文(Evan)

感谢@TJCrowder输入。但是,我相当确定我打算原样编写它。另外,链接中的第二个定义是:混杂意味着包括各种不同的事物。
埃文(Evan)

6

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

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

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

  2. 重命名hello.javamyclass.java

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


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
这不能回答问题。
David Conrad

2
@DavidConrad但是可以!答案为“'java'命令是否编译Java程序?” 由于hardlib在这里给出的原因,它是一个响亮的“是”:它将实时地将字节码编译为本地指令(对于具有标准设置的非平凡程序)。
彼得-恢复莫妮卡

现在是强制编译吗?从历史上讲,可以解释Java字节码。JIT编译是可选的。
MSalters

默认情况下,这几天(很长一段时间)默认情况下都启用了JIT,如mixed-mode版本输出中的所示
hardillb
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.