JavaFX: что это, как установить и написать первое окно

JavaFX: что это, как установить и написать первое окно Полезное

JavaFX — это библиотека (фреймворк) для настольных графических приложений на Java: окна, кнопки, таблицы, графики, анимация, стили на CSS и разметка интерфейса в FXML. С JDK 11 она не входит в состав JDK и развивается отдельно как открытый проект OpenJFX, поэтому ее подключают к проекту так же, как любую внешнюю зависимость.

Ниже — как JavaFX устроена, как ее поставить (SDK или Maven), первое окно, FXML с контроллером, CSS и разбор ошибок запуска. Все примеры проверены 24.09.2026 на JDK 25.0.4 (Temurin) и JavaFX 26.0.2 под Linux, в headless-режиме без монитора.

Коротко о терминах

Четыре слова путают чаще всего, поэтому сразу разведем их:

Термин Что это на самом деле
JavaFX Сам фреймворк: API, пакеты javafx.*
OpenJFX Открытый проект, в котором JavaFX разрабатывают; сборки на Maven Central называются org.openjfx
JavaFX SDK Архив с jar-файлами и нативными библиотеками под конкретную ОС
Stage / Scene / Node Окно / содержимое окна / любой элемент внутри (кнопка, панель, текст)

Удобная аналогия: Stage — рама картины, Scene — холст, Node — все, что на холсте нарисовано. Узлы складываются в дерево (scene graph): у корневой панели есть дочерние элементы, у тех свои. Это упрощение: одно окно может по ходу работы менять сцены, а сцена без окна существует, но на экран не выводится.

Откуда JavaFX и почему ее нет в JDK

Проект начинался как язык F3 Криса Оливера. После покупки SeeBeyond компанией Sun его переименовали в JavaFX, версия 1.0 вышла в декабре 2008 года и использовала отдельный язык JavaFX Script. В JavaFX 2.0 (2011, уже Oracle) скриптовый язык убрали: интерфейс стали писать на обычной Java или любом JVM-языке.

JavaFX 8 поставлялась вместе с JDK 8, поэтому старые учебники запускают ее без всяких настроек. Начиная с JDK 11 Oracle вынесла JavaFX из JDK: теперь это отдельный набор модулей (javafx.base, javafx.graphics, javafx.controls, javafx.fxml и другие) с собственным номером версии.

Номер версии JavaFX идет в ногу с JDK, но каждой версии нужен минимальный JDK (по release notes OpenJFX: 21 — JDK 17+, 25 — JDK 23+, 26 — JDK 24+, 27 — JDK 25+). Последний выпуск на сентябрь 2026 — JavaFX 27, линии 17, 21 и 25 — долгосрочные (LTS):

Ваш JDK Какую JavaFX брать
17 или 21 21.0.x (собрана под Java 17)
23-24 25.0.x (нужен JDK 23+) или 26.0.x (JDK 24+)
25 LTS 25.0.x, 26.0.x, 27 (27 требует JDK 25+)

Если взять JavaFX новее, чем позволяет JDK, приложение не запустится: по release notes OpenJFX лаунчер завершится с сообщением, что модуль javafx.base не может быть прочитан. Для JDK 8 отдельная JavaFX не нужна, если это сборка с JavaFX внутри.

Установка: Maven или SDK

Вариант 1: Maven (рекомендуемый для проектов). Maven сам скачает jar-файлы под вашу ОС и архитектуру, а плагин javafx-maven-plugin запустит приложение с правильными параметрами модулей.

<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>hellofx</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.release>25</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <javafx.version>26.0.2</javafx.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>${javafx.version}</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>${javafx.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.8</version>
                <configuration>
                    <mainClass>com.example.FxmlApp</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Запуск — mvn javafx:run. Обычный mvn exec:java или кнопка Run в IDE без настроек модулей приводят к ошибке из раздела ниже. В Gradle то же делает плагин org.openjfx.javafxplugin.

Вариант 2: SDK (для быстрых опытов без сборщика). Скачайте JavaFX SDK под свою ОС с сайта OpenJFX или Gluon, распакуйте и передайте путь к папке lib компилятору и JVM:

export PATH_TO_FX=/opt/javafx-sdk-26.0.2/lib
javac --module-path "$PATH_TO_FX" --add-modules javafx.controls -d out src/com/example/HelloFX.java
java --module-path "$PATH_TO_FX" --add-modules javafx.controls -cp out com.example.HelloFX

Путь /opt/javafx-sdk-26.0.2 — пример, подставьте свой. В Windows путь пишется как C:\javafx-sdk-26.0.2\lib, а вместо export — set PATH_TO_FX=....

Первое окно: Stage, Scene и кнопка

Минимальное полное приложение: метка и кнопка, которая считает нажатия.

package com.example;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class HelloFX extends Application {

    private int clicks = 0;

    @Override
    public void start(Stage stage) {
        Label label = new Label("Нажми кнопку");
        Button button = new Button("Нажми меня");
        button.setOnAction(e -> {
            clicks++;
            label.setText("Нажатий: " + clicks);
        });

        VBox root = new VBox(10, label, button);
        root.setPadding(new Insets(20));

        Scene scene = new Scene(root, 320, 160);
        stage.setTitle("Первое окно JavaFX");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Откроется окно 320×160 с заголовком «Первое окно JavaFX»; после двух нажатий метка покажет «Нажатий: 2». В headless-прогоне программно нажатая дважды кнопка дала именно это:

title=Первое окно JavaFX showing=true size=320.0x160.0
before: Нажми кнопку
after: Нажатий: 2

Что здесь происходит по шагам:

  1. launch(args) запускает среду JavaFX, создает главное окно и вызывает start(stage) в специальном потоке JavaFX Application Thread. Метод launch возвращает управление только после закрытия приложения.
  2. VBox — панель компоновки, ставит детей столбиком с промежутком 10 пикселей. Для строки есть HBox, для сетки GridPane, для «шапка-центр-низ» — BorderPane.
  3. Scene(root, 320, 160) создает содержимое окна с корневым узлом и начальным размером.
  4. stage.show() показывает окно. Без этой строки программа запустится, но окна не будет.

Одно правило стоит запомнить сразу: менять элементы интерфейса можно только из JavaFX Application Thread. Долгую работу (сеть, файлы) выносите в javafx.concurrent.Task, а результат возвращайте в интерфейс через Platform.runLater(...), иначе окно зависнет на время операции.

FXML и контроллер

Когда элементов становится много, разметку выносят в FXML — XML-файл с описанием дерева узлов, а логику оставляют в Java-классе-контроллере. Это разделение «вид — логика», примерно как HTML и JavaScript.

Файл src/main/resources/com/example/hello.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.VBox?>

<VBox xmlns:fx="http://javafx.com/fxml"
      fx:controller="com.example.HelloController"
      spacing="10" styleClass="root-box">
    <padding><Insets top="20" right="20" bottom="20" left="20"/></padding>
    <Label text="Как тебя зовут?"/>
    <TextField fx:id="nameField" promptText="Имя"/>
    <Button text="Поздороваться" onAction="#onGreet"/>
    <Label fx:id="greeting" styleClass="greeting"/>
</VBox>

Контроллер HelloController.java:

package com.example;

import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;

public class HelloController {

    @FXML
    private TextField nameField;

    @FXML
    private Label greeting;

    @FXML
    private void onGreet() {
        String name = nameField.getText().strip();
        greeting.setText(name.isEmpty() ? "Введи имя" : "Привет, " + name + "!");
    }
}

Связь устроена так: fx:controller называет класс, fx:id="nameField" попадает в поле с тем же именем и аннотацией @FXML, а onAction="#onGreet" вызывает одноименный метод. Имена должны совпадать буквально.

Главный класс загружает FXML и подключает стили:

package com.example;

import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class FxmlApp extends Application {

    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader loader = new FXMLLoader(FxmlApp.class.getResource("hello.fxml"));
        Parent root = loader.load();

        Scene scene = new Scene(root, 340, 200);
        scene.getStylesheets().add(
                FxmlApp.class.getResource("styles.css").toExternalForm());

        stage.setTitle("FXML + CSS");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Проверка: пустое поле и нажатие дает «Введи имя», ввод « Аня » дает «Привет, Аня!» (пробелы по краям срезает strip()). Визуальный редактор FXML — Scene Builder от Gluon, он генерирует такой же файл.

CSS в JavaFX

Стили JavaFX похожи на веб-CSS, но это отдельный диалект: свойства начинаются с префикса -fx-, а набор свойств свой. Файл styles.css лежит рядом с FXML:

.root-box {
    -fx-background-color: #f4f6fb;
    -fx-font-size: 14px;
}

.button {
    -fx-background-color: #3056d3;
    -fx-text-fill: white;
    -fx-background-radius: 6;
}

.greeting {
    -fx-text-fill: #1b7f3b;
    -fx-font-weight: bold;
}

Классы .button, .label, .text-field у стандартных элементов есть по умолчанию, свои (root-box, greeting) задаются через styleClass. В прогоне кнопка получила фон 0x3056d3ff, приветствие — цвет 0x1b7f3bff, то есть стили применились. Обычный CSS вроде color: red без префикса JavaFX молча проигнорирует.

Ошибки запуска и как их чинить

Ошибка 1: «JavaFX runtime components are missing». Самая частая. Главный класс наследует Application, а JavaFX лежит на classpath (или ее нет вовсе). Неверно:

java -cp "out:lib/*" com.example.HelloFX

Результат:

Error: JavaFX runtime components are missing, and are required to run this application

Исправление: передать JavaFX через module path и явно добавить модули, как в разделе про SDK, или запускать через mvn javafx:run. В IDE то же самое пишется в VM options конфигурации запуска: --module-path <путь к lib> --add-modules javafx.controls,javafx.fxml.

Встречается обходной путь — отдельный класс Launcher с main, который не наследует Application и вызывает HelloFX.main(args). Он запускается и с classpath, но JavaFX честно предупреждает:

WARNING: Unsupported JavaFX configuration: classes were loaded from 'unnamed module @311bd1df'

Для учебного проекта это терпимо, для рабочего лучше module path.

Ошибка 2: module path есть, модулей нет. Если указать --module-path, но забыть --add-modules, получим:

Error: Could not find or load main class com.example.HelloFX
Caused by: java.lang.NoClassDefFoundError: javafx/application/Application

А javac без module path вообще не видит пакеты: error: package javafx.application does not exist.

Ошибка 3: FXML не найден. Если hello.fxml не попал в ресурсы (лежит не в той папке или не в том пакете), getResource вернет null, и загрузчик скажет:

java.lang.IllegalStateException: Location is not set.

Файл должен лежать в src/main/resources/ по тому же пути пакета, что и класс: com/example/hello.fxml.

Ошибка 4: опечатка в имени обработчика. onAction="#onGret" вместо #onGreet:

javafx.fxml.LoadException: Error resolving onAction='#onGret', either the event handler is not in the Namespace or there is an error in the script.

Ошибка 5: модульный проект без opens. Если у приложения есть module-info.java, загрузчику FXML нужен рефлексивный доступ к приватным полям контроллера. Без него:

Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private javafx.scene.control.TextField com.example.HelloController.nameField accessible: module com.example does not "opens com.example" to module javafx.fxml

Исправление — строка opens в дескрипторе модуля:

module com.example {
    requires javafx.controls;
    requires javafx.fxml;

    exports com.example;
    opens com.example to javafx.fxml;
}

Еще одно сообщение пугает, но ошибкой не является: на JDK 24+ JVM пишет WARNING: A restricted method in java.lang.System has been called, потому что JavaFX загружает свои нативные библиотеки. Убрать его можно флагом --enable-native-access=javafx.graphics.

Выводы

  • JavaFX — отдельная от JDK библиотека для настольного GUI на Java; с JDK 11 ее подключают как зависимость из проекта OpenJFX.
  • Версию JavaFX выбирают под свой JDK: для JDK 17/21 — 21.0.x, для JDK 25 подходят 25.0.x-27.
  • Каркас приложения: Application.start(Stage) -> Scene с корневой панелью -> stage.show(); интерфейс меняют только из JavaFX Application Thread.
  • FXML отделяет разметку от логики: fx:id и #метод должны буквально совпадать с полями и методами контроллера, в модульном проекте нужен opens ... to javafx.fxml.
  • «JavaFX runtime components are missing» лечится запуском через module path с --add-modules или через mvn javafx:run.

Где применяется / связь с практикой

Освойте тему на практике

JavaFX берут для внутренних инструментов, редакторов, клиентов к базам и учебных проектов с интерфейсом; про более старую библиотеку Swing и работу со списками есть отдельный разбор — графический интерфейс в Java: списки и Swing. Чтобы уверенно собирать такие приложения, нужна база самого языка: классы, лямбды, модули, сборка через Maven. Ее системно дает курс Java разработчик. Базовый уровень, а попробовать формат можно на бесплатных открытых уроках Otus.

FAQ

Можно ли собрать из JavaFX-приложения обычный установщик?
Да: jlink собирает урезанную среду выполнения с нужными модулями, а jpackage из JDK упаковывает ее в установщик для Windows, macOS или Linux. Собирать нужно на той ОС, под которую делается пакет.

Работает ли JavaFX на телефонах и в браузере?
Не напрямую. Для Android и iOS существует отдельный инструментарий Gluon с нативной компиляцией, а в браузере JavaFX не запускается — апплеты и Java Web Start давно убраны.

Как проверить JavaFX-код на сервере без монитора?
Начиная с JavaFX 26 есть встроенная headless-платформа: запуск с -Dglass.platform=headless -Dprism.order=sw позволяет создать окно и нажимать кнопки программно, например в автотестах. Монитор не нужен, но системные библиотеки шрифтов нужны: в минимальном Linux-контейнере без pango запуск падает с UnsatisfiedLinkError: ... libpangoft2-1.0.so.0: cannot open shared object file. На более старых версиях для этого используют Monocle или виртуальный дисплей Xvfb.

OTUS Журнал
Скидка 5% 12-19 октября на курсы (popup)
Бесплатные открытые уроки (поп-ап)