Как запускается Java-программа
Команда java Main.java проверяет программу и сразу её запускает. Внутри она делает два шага. В этом уроке мы разделим их и посмотрим, как Java добирается от текста программы до строки в выводе.
Сначала о задаче, которую решают эти два шага. Процессор понимает только машинный код — набор команд, у каждого вида процессоров свой. Программа, переведённая в машинный код для одного компьютера, на другом может не запуститься: там другой процессор или другая операционная система.
Вспомните оркестр. Композитор записывает партитуру один раз, а играют её оркестры в разных городах. Java устроена так же: программу переводят один раз, а выполняют на любом компьютере.
public class Main {
public static void main(String[] args) {
System.out.println("Run anywhere"); // Run anywhere
}
}Сохраните эту программу в файл Main.java и наберите в терминале две команды по очереди:
$ 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:
$ 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.