Компилирует ли команда java Java-программы?

Большинство веб-сайтов в Интернете говорят:

"используйте команду javac для компиляции файла .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)

Ответ 1

До 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)

UPDATE:

Как отмечает @BillK, ОП также спросил:

зачем нам команда javac?

Причина, по которой нам нужен javac, заключается в создании файлов .class, чтобы код можно было создавать, тестировать, распространять, запускать, передавать и т.д., Как это делается сегодня. Мотивация для JEP 330 заключалась в том, чтобы облегчить "ранние этапы изучения Java и при написании небольших служебных программ" без изменения каких-либо других существующих применений.

Ответ 2

Если вы используете Java 11, есть новая функция, которая позволяет выполнять один исходный файл. Компилятор с одним исходным кодом более разнороден с точки зрения имени класса по сравнению с именем файла, поэтому вы можете работать, но не успешно компилировать.

Если вы используете предыдущую версию Java,, то ваш текущий hello.java не компилируется из-за ошибок компиляции, в частности, вокруг имени класса. Поэтому абсолютно невозможно, чтобы вызов java hello.java компилировал ваш код, потому что он не компилируется.

Наиболее вероятно, что вы выполняли какой-то ранее скомпилированный код при выполнении команды java.

Ответ 3

Да, но не так, как вы, вероятно, имеете в виду.

Когда вы используете команду javac для компиляции файла .java в файл .class, на выходе получается нечто, называемое байт-кодом. Байт-код - это машинный код (собственные инструкции) для теоретического ЦП, основанного на спецификации виртуальной машины Java.

Эта спецификация виртуального ЦП является своего рода средним числом типов ЦП, которые были распространены на момент написания спецификации. Из-за этого он близок к множеству разных типов процессоров, что облегчает запуск одних и тех же файлов Java.class на нескольких типах процессоров.

Когда Java запускался впервые, команда java считывала файл .class и интерпретировала инструкции байт-кода по одной, а затем отображала их в эквивалентную встроенную инструкцию для того процессора, на котором он фактически работал. Это сработало, но не особенно быстро. Для улучшения этой JIT-компиляции был добавлен Java Runtime.

С помощью JIT команда java берет байт-код и снова компилирует его в исходные инструкции для процессора, на котором он работает. Современные среды выполнения Java, как правило, начинают интерпретировать байт-код при JIT-компиляции в фоновом режиме и переключаются на скомпилированные нативные инструкции, когда он будет готов, а также профилируют работающее приложение, а затем заново компилируют байт-код с другой оптимизацией, чтобы получить наилучшую возможную производительность.

ОБНОВЛЕНИЕ (чтобы успокоить избирателей):

Таким образом, в вашем конкретном случае (поскольку вы используете JRE новее, чем v11), код компилируется (как минимум) дважды

  1. Как один файл .java для байт-кода
  2. С помощью JIT-компилятора, поскольку он интерпретирует байт-код (хотя для helloWorld у него может не хватить времени для запуска любого скомпилированного нативного кода)

Ответ 4

Чтобы ответить, почему возникает эта ошибка, имя класса для файла должно соответствовать файлу basename.

У вас есть два варианта, чтобы этот код работал для традиционного javac; Последовательность java:

  1. Переименуйте класс в public class Hello или

  2. Переименуйте hello.java в myclass.java.

Интерпретатор java для Java 11 не накладывает этого требования. Класс, который содержит main, может иметь любое имя, если это первый класс в файле. Это было главным образом предназначено, чтобы упростить процесс обучения для начинающих, и позволить "сценарии Java" с шебангом (ref.).

Ответ 5

Эта функция позволяет запускать однофайловый исходный код Java напрямую без какой-либо компиляции, избегая утомительных шагов, которые ранее требовались для запуска простой программы hello world.

Java 11+ Launch Single-File Source-Code Programs 

Зачем тебе это нужно Если вы вспомните старые времена незадолго до Java SE 11 (JDK 11), скажем, у вас есть исходный файл HelloUniverse.java, который содержит определение класса и статический метод main, который выводит в терминал одну строку текста, как показано ниже :

public class HelloUniverse{
      public static void main(String[] args) { 
            System.out.println("Hello InfoQ Universe");
      }
}

Обычно для запуска этого класса сначала необходимо скомпилировать его с помощью компилятора Java (javac), что приведет к созданию файла HelloUniverse.class:

$ javac HelloUniverse.java
Then you would use a java virtual machine (interpreter) command to run the resulting class file:

$ java HelloUniverse
Hello InfoQ Universe

Это запускает JVM, загружает класс и выполняет код.

Но что, если вы хотите быстро протестировать кусок кода и хочу поэкспериментировать. Эти два шага в процессе могут показаться немного тяжелыми.   В Java SE 11 вы можете запустить один файл исходного кода напрямую, без промежуточной компиляции.