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
Что здесь происходит по шагам:
launch(args)запускает среду JavaFX, создает главное окно и вызываетstart(stage)в специальном потоке JavaFX Application Thread. Методlaunchвозвращает управление только после закрытия приложения.VBox— панель компоновки, ставит детей столбиком с промежутком 10 пикселей. Для строки естьHBox, для сеткиGridPane, для «шапка-центр-низ» —BorderPane.Scene(root, 320, 160)создает содержимое окна с корневым узлом и начальным размером.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.



