Перейти к содержимому
Теория · Первая программа · урок 3 из 8

Как запускается Java-программа

Команда java Main.java проверяет программу и сразу её запускает. Внутри она делает два шага. В этом уроке мы разделим их и посмотрим, как Java добирается от текста программы до строки в выводе.

Сначала о задаче, которую решают эти два шага. Процессор понимает только машинный код — набор команд, у каждого вида процессоров свой. Программа, переведённая в машинный код для одного компьютера, на другом может не запуститься: там другой процессор или другая операционная система.

Вспомните оркестр. Композитор записывает партитуру один раз, а играют её оркестры в разных городах. Java устроена так же: программу переводят один раз, а выполняют на любом компьютере.

JAVA
public class Main {
    public static void main(String[] args) {
        System.out.println("Run anywhere"); // Run anywhere
    }
}

Сохраните эту программу в файл Main.java и наберите в терминале две команды по очереди:

TEXT
$ javac Main.java
$ java Main
Run anywhere

Разберём, что делает каждая.

Шаг 1: javac пишет байт-код

javac — компилятор Java. Он проверяет текст и переводит его в байт-код — инструкции не для процессора, а для особой программы, JVM. Результат он кладёт рядом, в файл Main.class. javac программу не запускает.

Байт-код и есть партитура из первого урока, переписанная начисто: её записывают один раз, и она подходит любому компьютеру. Заглянуть в него можно командой javap -c Main. В методе main нашей программы четыре инструкции:

  • getstatic достаёт System.out;
  • ldc берёт строку "Run anywhere";
  • invokevirtual вызывает println;
  • return завершает метод.

Шаг 2: JVM выполняет байт-код

Команда java Main запускает JVM — виртуальную машину Java. Это программа, которая читает байт-код из Main.class и выполняет его, переводя инструкции в команды процессора этого компьютера. Обратите внимание: команде нужно имя класса Main, а не имя файла.

JVM у каждой операционной системы своя: для Windows, macOS, Linux. А байт-код один на всех. Отсюда девиз Java: «написано один раз — работает везде». Main.class, скомпилированный в Windows, запустится в Linux без перекомпиляции.

Этот вопрос часто задают на собеседовании: почему Java не зависит от платформы — операционной системы и процессора? Ответ состоит из двух половин: переносим байт-код, а JVM у каждой платформы своя.

Здесь картинка с оркестром неточна. Каждый оркестр играет пьесу немного по-своему, а JVM на любом компьютере выполняет байт-код одинаково: программа напечатает тот же текст.

JDK, JRE и JVM

Ещё один классический вопрос собеседования — чем отличаются эти три сокращения. Они вложены друг в друга:

  • JVM выполняет байт-код;
  • JRE — это JVM вместе со стандартной библиотекой, готовым кодом вроде System.out.println; этого хватает, чтобы запускать программы;
  • JDK — это JRE вместе с инструментами разработчика: компилятором javac, программой javap и упаковщиком jar, который собирает .class-файлы в один архив.

Писать программы можно только с JDK: javac есть лишь в нём.

Два шага одной командой

Команда java Main.java делает оба шага сама: компилирует программу в памяти и сразу запускает её. Файл Main.class при этом не появляется. Внутри она тоже вызывает компилятор, поэтому и ей нужен JDK.

Для программы из одного файла это удобно. А javac нужен, когда байт-код надо сохранить, чтобы запускать его потом или на других компьютерах.

Частая ошибка

Запустили класс по имени файла, вместе с .class:

TEXT
$ java Main.class
Error: Could not find or load main class Main.class

Команда java ждёт имя класса и сама находит для него файл Main.class. Получив Main.class, она ищет класс с таким странным именем и не находит его. Правильная команда — java Main, без расширения.

Главное

  • javac Main.java переводит программу в байт-код и сохраняет его в Main.class; программу он не запускает.
  • java Main запускает JVM, и она выполняет байт-код; JVM у каждой платформы своя, а байт-код один для всех.
  • JVM выполняет байт-код, JRE добавляет к ней стандартную библиотеку, а JDK — ещё и javac с другими инструментами.
  • java Main.java компилирует программу в памяти и сразу запускает её, не создавая Main.class.