Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
210: Introduction to JavaFX
You've spent a lot of time in the console so far, but now we're moving into the visual side of things with JavaFX. If you've ever tried to build a GUI in Java, you know it can feel a bit like magic until something breaks. When it does break, it usually breaks in a way that makes no sense at first glance.
Take a look at this snippet. I've written a simple app that's supposed to simulate a long-running task—like downloading a file—and update a label when it's finished. On the surface, it looks perfectly logical.
public class DownloaderApp extends Application {
@Override
public void start(Stage primaryStage) {
Label statusLabel = new Label("Ready to download...");
Button startButton = new Button("Start Download");
startButton.setOnAction(e -> {
// We start a new thread so the UI doesn't freeze during the "download"
new Thread(() -> {
try {
Thread.sleep(3000); // Simulate a 3-second download
statusLabel.setText("Download Complete!");
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}).start();
});
VBox root = new VBox(10, statusLabel, startButton);
primaryStage.setScene(new Scene(root, 300, 200));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The dreaded Not on FX application thread error
If you run this, the app will launch, the button will click, and then—three seconds later—your console will explode with an IllegalStateException: Not on FX application thread. The program crashes, and the label never updates.
Here is what's happening: JavaFX, like almost every modern GUI toolkit, is single-threaded. There is one specific thread—the JavaFX Application Thread—that owns the "scene graph" (all your buttons, labels, and layouts). If you try to change the text of a label or the color of a button from any other thread, JavaFX throws a fit and shuts the operation down to prevent memory corruption and unpredictable rendering glitches.
I used a new Thread() to prevent the UI from freezing (which is the right instinct), but then I tried to reach back into the UI from that background thread to update the label. That's the forbidden move.
Wrapping UI updates in Platform.runLater()
To fix this, we need a way to tell JavaFX: "Hey, I'm doing work in the background, but when you have a spare millisecond, please run this specific piece of code on your own main thread."
That's exactly what Platform.runLater() does. It takes a Runnable and queues it up for the UI thread to execute as soon as possible.
// Inside the Thread's run method:
new Thread(() -> {
try {
Thread.sleep(3000);
// This is the fix: wrap the UI update in Platform.runLater()
Platform.runLater(() -> {
statusLabel.setText("Download Complete!");
});
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}).start();
By wrapping the setText() call, we aren't forcing the update from the background thread; we're sending a request to the UI thread to handle it. The background thread keeps running its own business, and the UI thread handles the visual update when it can. This keeps your app responsive and, more importantly, keeps it from crashing.
Structuring your JavaFX Application
Aside from the threading issues, you'll notice a specific structure in JavaFX apps. You don't just start with a main method that does everything. Instead, you extend the Application class and override the start(Stage primaryStage) method.
- The Stage: Think of this as the window frame. It handles the minimize/maximize buttons and the window title.
- The Scene: This is the content inside the window. You can swap scenes out (like switching from a login screen to a dashboard) while keeping the same stage.
- The Scene Graph: This is the hierarchy of nodes (Buttons, Labels, Layouts) that make up your scene. I used a
VBoxin the example, which simply stacks elements vertically.
When you call launch(args) in your main method, JavaFX handles all the heavy lifting of initializing the toolkit and eventually calling your start method on that precious Application Thread we talked about.
📋 Practical Task
Exercise: Building a Real-Time System Monitor
Your goal is to create a JavaFX application that simulates a system resource monitor. The application should have a ProgressBar and a Label showing the current percentage.
Requirements:
- Create a window (Stage) with a title "System Monitor".
- Add a
ProgressBarand aLabelto the layout. - Implement a background thread that starts immediately when the app launches.
- This thread should "simulate" CPU usage by generating a random double between 0.0 and 1.0 every 500 milliseconds.
- Use
Platform.runLater()to update theProgressBarprogress and theLabeltext (e.g., "CPU Usage: 45%") based on the random value. - Ensure the UI remains responsive while the updates are happening in the background.
There are no comments for now.