JavaFX и GUI
JavaFX и GUI
Desktop GUI в Java — JavaFX, Swing и альтернативы
После консольных программ (первая программа, основные конструкции) часто нужен графический интерфейс — окно, кнопки, формы, диалоги. В Java для этого есть несколько поколений библиотек: Swing в составе JDK, JavaFX (OpenJFX) как рекомендуемый современный стек, плюс специализированные фреймворки.
Эта статья — обзор экосистемы и примеры виджетов; пошаговый старт — 3111.md, рецепты по каждому элементу UI — 3112.md. Swing без Maven — практические примеры. Общая картина платформы — Экосистема Java-приложений. Теория десктопа — Архитектура десктопных приложений.
Если вы только переходите из консольных программ в GUI, полезно читать материал слоями: сначала понять архитектуру и цикл событий, затем освоить базовые контролы, после этого перейти к компоновке и обработке событий. Такой порядок даёт прочный фундамент и снимает ощущение «случайного набора API».
Быстрый маршрут по статье
- Пройти первую программу на JavaFX или минимальный пример с кнопкой ниже.
- Разобраться, зачем нужен
Application.launchи поток JavaFX. - Освоить контейнеры компоновки (
VBox,HBox,GridPane,BorderPane). - Добавить обработчики через
setOnActionи свойства с привязкой. - Сравнить тот же сценарий на Swing (EDT,
ActionListener). - Закрепить на мини-проектах в конце статьи.
Связанные главы: Практические примеры — Swing, Экосистема Java-приложений, JVM, память и потоки, Maven и структура проекта, Особенности десктопа.
GUI в Java
Если задача требует полноценного приложения с кнопками, полями ввода, меню и другими элементами управления, используют специализированные библиотеки для построения GUI.
Графический пользовательский интерфейс (Graphical User Interface) — система, через которую пользователь взаимодействует с программой через визуальные компоненты: окна, кнопки, списки, диаграммы. В Java доступны несколько основных направлений:
- Swing (
javax.swing) — библиотека в составе JDK; рисует «лёгкие» компоненты на Java, без нативных peer-объектов AWT. - JavaFX (
javafx.*, проект OpenJFX) — современный стек с CSS, FXML, анимациями и аппаратным ускорением; с Java 11 подключается отдельной зависимостью. - AWT (
java.awt) — первая графическая библиотека Java на нативных виджетах ОС; сегодня служит основой для Swing и системных диалогов. - SWT (Eclipse) — нативные контролы Windows/macOS/Linux; часто встречается в Eclipse IDE.
- Compose Multiplatform (JetBrains) — декларативный UI на Kotlin; для чистого Java-стека реже, но полезен при миграции на Kotlin.
- Vaadin — веб-интерфейс на Java без HTML/JS в коде приложения; сервер рендерит UI в браузер.
Любое GUI-приложение следует событийно-ориентированной архитектуре:
- создаётся главное окно;
- добавляются виджеты (элементы управления);
- регистрируются обработчики событий (клик, ввод текста, изменение размера);
- запускается главный цикл UI, который принимает сообщения от ОС и распределяет их по обработчикам.
Сравнение основных стеков
| Swing | JavaFX | SWT | |
|---|---|---|---|
| В составе JDK | Да | Нет (OpenJFX) | Нет |
| Стилизация | Look-and-Feel, UIManager | CSS, темы | Нативный вид ОС |
| Разметка UI | Код + LayoutManager | Код, FXML | Код |
| Анимации | Ограниченно | Timeline, Transition | Минимально |
| Типичный старт | Учебные проекты, legacy | Новые desktop-приложения | Eclipse, RCP |
Для новых desktop-проектов на Java чаще выбирают JavaFX. Swing остаётся удобным первым шагом: нулевая настройка зависимостей, миллионы строк legacy-кода в enterprise, простые утилиты.
Архитектура JavaFX
JavaFX строит интерфейс как дерево узлов (Node) внутри сцены (Scene), отображаемой в окне (Stage). Это ближе к современным UI-фреймворкам (Qt, WPF), чем к раннему AWT.
| Компонент | Назначение |
|---|---|
Stage | Главное (или дочернее) окно приложения |
Scene | Контейнер сцены с корневым узлом и размерами |
Node | Любой визуальный элемент — Button, Label, TextField, контейнер |
Parent | Узел, который может содержать дочерние (VBox, GridPane) |
Application | Точка входа; инициализирует платформу и вызывает start(Stage) |
| JavaFX Application Thread | Единственный поток, где можно менять UI |
Пример на JavaFX:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class HelloFx extends Application {
@Override
public void start(Stage stage) {
Label prompt = new Label("Введите имя:");
TextField nameField = new TextField();
Label greeting = new Label();
Button greetButton = new Button("Приветствовать");
greetButton.setOnAction(e -> {
String name = nameField.getText().trim();
greeting.setText(name.isEmpty() ? "Введите имя" : "Привет, " + name + "!");
});
VBox root = new VBox(10, prompt, nameField, greetButton, greeting);
root.setStyle("-fx-padding: 20;");
Scene scene = new Scene(root, 360, 200);
stage.setTitle("Пример GUI");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Разбор:
Application.launch(args)поднимает JavaFX runtime и вызываетstart.VBox— вертикальный контейнер;10— расстояние между дочерними узлами.setOnActionсвязывает кнопку с обработчиком клика (лямбда).Sceneзадаёт корень и размер окна;stage.show()отображает окно.
launch блокирует main до закрытия последнего окна — аналог mainloop() в Tkinter или app.exec() в Qt.
Иерархия Stage → Scene → Node
Stage (окно)
└── Scene (400×300)
└── Parent (корень, например BorderPane)
├── top: ToolBar
├── center: TableView
└── bottom: HBox с кнопками
Каждый Node имеет свойства (положение, видимость, стиль), которые можно менять из кода или привязать к другим свойствам. Изменения в UI выполняются только в JavaFX Application Thread — прямой вызов из фонового потока приведёт к IllegalStateException.
Пример простого приложения с кнопкой
Для кнопки с обработчиком используют Button и setOnAction. В отличие от Swing, здесь передаётся лямбда или метод, а не экземпляр ActionListener:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ButtonDemo extends Application {
private void onButtonClick() {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Событие");
alert.setHeaderText(null);
alert.setContentText("Кнопка была нажата!");
alert.showAndWait();
}
@Override
public void start(Stage stage) {
Button button = new Button("Нажми меня");
button.setOnAction(e -> onButtonClick());
button.setMaxWidth(Double.MAX_VALUE);
button.setStyle("-fx-font-size: 14px; -fx-padding: 10 20;");
StackPane root = new StackPane(button);
root.setStyle("-fx-padding: 20;");
stage.setTitle("Пример кнопки JavaFX");
stage.setScene(new Scene(root, 320, 180));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Ключевые аспекты:
setOnAction— обработчик срабатывает при активации кнопки (клик или Enter при фокусе).Alert— стандартный диалог;showAndWait()блокирует до закрытия (модальный режим).launch— без него окно не появится; код послеlaunchвmainвыполнится только после завершения приложения.- Стили — строка CSS с префиксом
-fx-(шрифт, отступы, цвета).
Инициализация приложения
Минимальный каркас:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
public class MinimalApp extends Application {
@Override
public void start(Stage stage) {
stage.setTitle("Приложение");
stage.setScene(new Scene(new Label("Hello"), 400, 300));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Класс наследует Application и переопределяет start. Альтернатива без наследования — Application.launch() с анонимным подклассом, но для учебных проектов достаточно явного класса.
Подключение JavaFX через Maven
Фрагмент pom.xml для Java 17+ и OpenJFX 21:
<properties>
<javafx.version>21.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.HelloFx</mainClass>
</configuration>
</plugin>
</plugins>
</build>
Запуск:
mvn javafx:run
Подробнее о структуре Maven-проекта — Структуры проекта.
Контролы JavaFX
Control — интерактивный узел: кнопка, поле ввода, чекбокс. Label — статический текст или изображение.
Объект Label создаётся так:
Label label = new Label("Приветствие");
label.setStyle("-fx-text-fill: blue; -fx-background-color: white; -fx-padding: 8;");
Таблица частых свойств Label и аналогов:
| Свойство / метод | Описание | Пример |
|---|---|---|
setText(String) | Текст метки | "Информация" |
textProperty() | Свойство для привязки | label.textProperty().bind(...) |
setFont(Font) | Шрифт | Font.font("Arial", FontWeight.BOLD, 14) |
setWrapText(true) | Перенос длинных строк | для узких панелей |
setGraphic(Node) | Иконка рядом с текстом | ImageView |
setAlignment(Pos) | Выравнивание внутри Label | Pos.CENTER_LEFT |
setMaxWidth(Double.MAX_VALUE) | Растягивание по ширине контейнера | в VBox с setFillWidth(true) |
Методы, общие для всех Node:
| Метод | Описание |
|---|---|
setVisible(boolean) | Показать или скрыть |
setDisable(boolean) | Отключить ввод, «серый» вид |
setOnMouseClicked(...) | Обработчик клика мыши |
getStyleClass().add("...") | CSS-класс из таблицы стилей |
Пример оформленных меток:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
public class LabelDemo extends Application {
@Override
public void start(Stage stage) {
Label title = new Label("Заголовок окна");
title.setFont(Font.font("Arial", FontWeight.BOLD, 18));
title.setStyle("-fx-text-fill: white; -fx-background-color: black; -fx-padding: 12;");
Label info = new Label("Это информационная метка.\nТекст может быть многострочным.");
info.setWrapText(true);
info.setMaxWidth(280);
info.setAlignment(Pos.CENTER_LEFT);
VBox root = new VBox(16, title, info);
root.setStyle("-fx-padding: 20;");
stage.setScene(new Scene(root, 360, 220));
stage.setTitle("Label");
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Динамическое обновление текста и привязки
Прямой вызов setText работает, но для связи нескольких виджетов удобнее properties и bindings:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class BindingDemo extends Application {
@Override
public void start(Stage stage) {
TextField field = new TextField();
field.setPromptText("Введите текст");
Label mirror = new Label();
mirror.textProperty().bind(
Bindings.concat("Вы ввели: ", field.textProperty())
);
Button clear = new Button("Очистить");
clear.setOnAction(e -> field.clear());
stage.setScene(new Scene(new VBox(10, field, mirror, clear), 360, 160));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
textProperty().bind(...) автоматически обновляет метку при каждом изменении поля — без ручного setText в обработчике.
Контейнеры компоновки
Размещение узлов на сцене задают layout-контейнеры (Pane и наследники). В одном родителе используют один тип менеджера; смешивать произвольно нельзя (дочерний узел принадлежит одному Parent).
VBox — вертикальный поток:
VBox box = new VBox(8); // 8 px между детьми
box.getChildren().addAll(label, field, button);
box.setAlignment(Pos.CENTER);
VBox.setVgrow(table, Priority.ALWAYS); // таблица забирает свободное место
HBox — горизонтальный поток; параметры spacing, alignment, HBox.setHgrow.
GridPane — сетка строк и столбцов:
| Метод | Описание |
|---|---|
add(node, column, row) | Ячейка сетки |
add(node, col, row, colspan, rowspan) | Объединение ячеек |
setHgap / setVgap | Расстояние между ячейками |
GridPane.setConstraints(node, ...) | Прилипание к краям (GridPane.setHgrow) |
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(8);
grid.add(new Label("Имя:"), 0, 0);
grid.add(nameField, 1, 0);
grid.add(new Label("Пароль:"), 0, 1);
grid.add(passwordField, 1, 1);
BorderPane — зоны top, bottom, left, right, center (классическая форма с меню и центральной областью):
BorderPane root = new BorderPane();
root.setTop(toolBar);
root.setCenter(tableView);
root.setBottom(statusBar);
StackPane — наложение слоёв (кнопка по центру, фон, оверлей загрузки).
AnchorPane, FlowPane, TilePane — для специальных случаев; в формах чаще GridPane или VBox/HBox.
Обработка событий
JavaFX следует event-driven модели. Платформа получает сообщения от ОС, ставит их в очередь и вызывает зарегистрированные обработчики в UI-потоке.
Основные способы подписки:
button.setOnAction(e -> { /* клик / Enter */ });
node.setOnMouseClicked(e -> {
double x = e.getX();
double y = e.getY();
});
field.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ENTER) submit();
});
stage.widthProperty().addListener((obs, oldVal, newVal) -> resizeContent());
Для фильтрации и всплытия есть цепочка event filter (сверху вниз) и event handler (снизу вверх). Для большинства учебных задач достаточно setOnAction и setOnMouseClicked.
Controls (кнопки, поля) генерируют высокоуровневые события; для низкоуровневого ввода — MouseEvent, KeyEvent, ScrollEvent.
FXML и контроллер
Интерфейс можно описать в FXML (XML-разметка) и связать с Java-классом controller:
hello.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="com.example.HelloController"
spacing="10" style="-fx-padding: 20;">
<Label text="Конвертер °C → °F"/>
<TextField fx:id="celsiusField" promptText="Температура °C"/>
<Button text="Перевести" onAction="#convert"/>
<Label fx:id="resultLabel"/>
</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 celsiusField;
@FXML
private Label resultLabel;
@FXML
private void convert() {
try {
double c = Double.parseDouble(celsiusField.getText().trim().replace(',', '.'));
double f = c * 9 / 5 + 32;
resultLabel.setText(String.format("%.1f °C = %.1f °F", c, f));
} catch (NumberFormatException ex) {
resultLabel.setText("Введите число");
}
}
}
Загрузка в Application.start:
FXMLLoader loader = new FXMLLoader(getClass().getResource("/hello.fxml"));
Parent root = loader.load();
stage.setScene(new Scene(root, 360, 200));
Такой подход отделяет разметку от логики — аналог .ui в Qt Designer или отдельного ui.py в Tkinter-проектах.
CSS-стилизация
JavaFX понимает CSS с префиксом -fx-. Стили задают inline (setStyle), через файл styles.css или классы:
scene.getStylesheets().add(getClass().getResource("/app.css").toExternalForm());
.button-primary {
-fx-background-color: #2563eb;
-fx-text-fill: white;
-fx-font-weight: bold;
}
.error-label {
-fx-text-fill: #dc2626;
}
Тема Modena включена по умолчанию; для кастомного вида переопределяют селекторы .root, .button, .text-field.
Swing — встроенный GUI JDK
Swing (javax.swing) рисует компоненты на Java поверх AWT. Архитектура окна:
JFrame (главное окно)
└── contentPane
├── JLabel, JButton, JTextField
└── JPanel + LayoutManager (FlowLayout, BorderLayout, GridLayout, BoxLayout)
Графический интерфейс Swing работает в Event Dispatch Thread (EDT). Создание и изменение компонентов выполняют через SwingUtilities.invokeLater():
import javax.swing.*;
public class SimpleWindow {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Моё первое окно");
frame.setSize(400, 300);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JLabel("Привет, Swing!", SwingConstants.CENTER));
frame.setVisible(true);
});
}
}
Обработка клика — ActionListener (или лямбда):
JButton button = new JButton("Нажми меня");
button.addActionListener(e -> JOptionPane.showMessageDialog(frame, "Клик!"));
Полные примеры счётчика и формы входа — в Практические примеры — Swing.
Геометрические менеджеры Swing
| Менеджер | Поведение | Когда использовать |
|---|---|---|
FlowLayout | Элементы в ряд, перенос строк | Простые панели кнопок |
BorderLayout | NORTH, SOUTH, EAST, WEST, CENTER | Классическое окно с toolbar и центром |
GridLayout | Равномерная сетка | Формы с одинаковыми ячейками |
GridBagLayout | Гибкая сетка с весами | Сложные формы |
BoxLayout | Вертикальный или горизонтальный стек | Списки полей |
Важно: внутри одного контейнера нельзя смешивать менеджеры — у каждого JPanel один LayoutManager.
Пример BorderLayout:
JPanel panel = new JPanel(new BorderLayout(8, 8));
panel.add(toolbar, BorderLayout.NORTH);
panel.add(scrollPane, BorderLayout.CENTER);
panel.add(statusLabel, BorderLayout.SOUTH);
Swing и потоки
Swing, как и JavaFX, не потокобезопасен. Обновление UI из фонового потока:
SwingUtilities.invokeLater(() -> label.setText("Готово"));SwingWorker— для длительных задач с прогрессом и публикацией результата в EDT.
В JavaFX аналог — Platform.runLater(...) и класс Task / Service.
Пример безопасного обновления из фонового потока (JavaFX):
new Thread(() -> {
String result = loadFromNetwork(); // долго
Platform.runLater(() -> statusLabel.setText(result));
}).start();
Для сетевых и файловых операций в GUI сверяйтесь с работой с БД и общими принципами многопоточности.
AWT — кратко
AWT (java.awt) использует peer-объекты — нативные виджеты Windows, macOS или X11. Внешний вид зависит от ОС; кастомизация ограничена. Сегодня AWT применяют там, где Swing/JavaFX опираются на низкий уровень: системный трей, некоторые диалоги, работа с Graphics, Robot, буфер обмена. Новые экранные формы на чистом AWT писать редко — разумнее Swing или JavaFX.
Организация кода в реальном приложении
В учебных примерах всё в одном классе; в утилитах удобнее разделить ответственность:
MainApp/Application— запуск, конфигурация Stage;view— FXML или фабрики панелей;controller— обработчики событий, связь view и model;service— логика без UI (файлы, HTTP, БД);model— данные и правила предметной области.
Такой каркас упрощает тестирование сервисов без поднятия UI и повторное использование логики в CLI или REST.
// MainApp.java — только сборка
public class MainApp extends Application {
@Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/main.fxml"));
Parent root = loader.load();
stage.setScene(new Scene(root));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
Частые анти-паттерны
- Долгая операция прямо в
setOnAction/ActionListenerбезTask,SwingWorkerили фонового потока — UI «замирает». - Обновление виджетов из worker-потока без
Platform.runLater/invokeLater. - Глобальные статические ссылки на все контролы вместо явной структуры приложения.
- Смешивание нескольких layout-менеджеров на одном
JPanelбез вложенных панелей. - Отсутствие валидации ввода перед парсингом
Double.parseDoubleи SQL. - Игнорирование
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)— процесс JVM остаётся висеть.
Примеры реализации
Таймер обратного отсчёта (JavaFX)
Timeline планирует периодические обновления в UI-потоке:
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class CountdownApp extends Application {
private int count = 10;
@Override
public void start(Stage stage) {
Label label = new Label("Осталось: " + count);
label.setStyle("-fx-font-size: 24px;");
Timeline timeline = new Timeline();
timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(1), e -> {
count--;
label.setText(count > 0 ? "Осталось: " + count : "Время вышло!");
if (count <= 0) {
timeline.stop();
}
}));
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
stage.setScene(new Scene(new StackPane(label), 300, 120));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Форма входа с валидацией (JavaFX)
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class LoginFormFx extends Application {
@Override
public void start(Stage stage) {
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20));
TextField nameField = new TextField();
PasswordField passwordField = new PasswordField();
Label result = new Label(" ");
Button submit = new Button("Войти");
submit.setOnAction(e -> {
String name = nameField.getText().trim();
String password = passwordField.getText();
if (name.isEmpty()) {
new Alert(Alert.AlertType.ERROR, "Введите имя").showAndWait();
} else if (password.length() < 3) {
new Alert(Alert.AlertType.ERROR, "Пароль слишком короткий").showAndWait();
} else {
result.setText("Привет, " + name + "!");
nameField.clear();
passwordField.clear();
}
});
grid.add(new Label("Имя:"), 0, 0);
grid.add(nameField, 1, 0);
grid.add(new Label("Пароль:"), 0, 1);
grid.add(passwordField, 1, 1);
grid.add(submit, 1, 2);
grid.add(result, 1, 3);
stage.setScene(new Scene(grid, 400, 200));
stage.setTitle("Вход");
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Таблица и меню (Swing)
Для data-heavy UI в legacy-стеке часто используют JTable + TableModel:
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class TableDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
String[] columns = {"Имя", "Возраст"};
Object[][] data = {{"Алиса", 30}, {"Боб", 25}};
JTable table = new JTable(new DefaultTableModel(data, columns));
JFrame frame = new JFrame("Таблица");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(table));
frame.setSize(400, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
В JavaFX аналог — TableView с ObservableList и колонками PropertyValueFactory.
Частые ошибки
| Симптом | Причина |
|---|---|
| Окно не появляется | Нет launch() / setVisible(true) |
IllegalStateException: Not on FX application thread | UI меняют из фонового потока без Platform.runLater |
Module javafx.controls not found | OpenJFX не добавлен в module path / Maven |
| JVM не завершается после закрытия окна Swing | Нет EXIT_ON_CLOSE или остались не-daemon потоки |
| UI зависает | Блокирующий код в обработчике кнопки |
| FXML не находит controller | Неверный fx:controller или ресурс не в resources |
Что попробовать
- Тот же сценарий «приветствие» на Swing и JavaFX — сравните EDT и JavaFX thread.
- Вынести разметку в FXML и подключить CSS-файл.
- Долгую задачу обернуть в
Taskс индикаторомProgressIndicator. - Собрать JAR с
javafx-maven-pluginи запустить на другой машине с JRE 17+. - Общую теорию окон и mainloop — Архитектура десктопных приложений.
Практика и справочник
- Первая программа на JavaFX
- Справочник JavaFX и Swing — элементы UI
- Практические примеры — Swing
- Экосистема Java-приложений — UI
- Maven и JavaFX
- JVM и потоки
- Архитектура десктопных приложений · раздел «Десктопные приложения» · 112.md
mainloop и Tcl/Tk, здесь JavaFX Application Thread и OpenJFX. Оба материала опираются на одну и ту же теорию десктопа.См. также
Другие статьи этого же раздела в боковом меню (как на странице "О разделе"). Основы Java - устройство JDK/JVM, модель компиляции и базовые принципы платформонезависимого выполнения. Java — объектно-ориентированный язык общего назначения с принципом «напиши один раз, запускай в любом месте». Набор советов, правил, принципов и обычаев в разработке на этом языке. История Java — от проекта Green и Oak до OpenJDK, LTS-релизов и современной платформы (модули, records, виртуальные потоки). Библиотеки, фреймворки, инструменты сборки, тестирования, развёртывания и мониторинга. Что такое пакет и пакетная структура, как собираются проекты на Java. Справочник-шпаргалка по конфигурациям в Java — типы, синтаксис, стандартная библиотека, типовые паттерны. Не заменяет пошаговое обучение. Учебный курс — раздел. Гайд по установке и настройке с написанием первой программы и её запуском. Практические примеры — консольные утилиты, композиция классов и первое Swing-приложение. Точки останова, пошаговое выполнение, панели Variables и Call Stack — практика отладки в IntelliJ IDEA. Кавычки, точки, запятые, скобки и прочие знаки препинания. Это полный справочник всех ключевых слов языка Java, включая основные, контекстные и зарезервированные слова.Основы языка Java
Что требуется знать перед началом изучения языка программирования Java
Рекомендации по разработке на Java
История языка Java
Экосистема Java-приложений
Структура и сборки Java-проектов
Справочник по конфигурациям в Java
Первая программа на Java
Простые приложения на Java
Отладка Java-кода в IDE
Синтаксис и пунктуация в Java
Ключевые слова в Java