Learn Java

Lesson 77Elective: Desktop apps with JavaFX

Creating and running a JavaFX application

The Application class, the three steps inside start(), the lifecycle, and two complete programs compiled and run from the command line and VS Code.

By Updated 65 min

  • Application
  • start()
  • launch()
  • Group
  • ObservableList
  • Scene

The last lesson described a JavaFX program from the outside: a Stage holding a Scene holding a tree of nodes. This lesson writes one. You will build two programs, a brown window 600 pixels wide and a window that says “Welcome to Ooty”, then compile and run them yourself from a terminal, knowing what every word of the long javac and java commands does.

By the end of this lesson
  • Write the skeleton every JavaFX program shares: a class that extends Application, overrides start, and calls launch from main.
  • Build a scene graph in a Group, wrap it in a Scene of a chosen size, and show it on the Stage.
  • Trace the lifecycle (constructor, init, start, stop), say which thread runs each, and end a program the right way.
  • Compile and run a JavaFX program from the command line, explain every flag, and fix the error each missing flag causes.
  • Place text in a window at a chosen point, and explain why that point is the text's baseline.
From before
Compiling with javac and running with java are two separate steps (setup and the JVM), and today each one has to be told where JavaFX lives. Your program class extends a library class and overrides one of its methods. main can call launch without an object because launch is static. The classpath from packages and imports is where java looks for your own classes, and the lifecycle runs on more than one thread.

Creating a JavaFX application#

Every JavaFX program is built around one library class, Application in the package javafx.application. It is the entry point of a JavaFX application, and your program is a class that inherits from it.

You extend Application; you never create it with new
Application is an abstract class: its method start(Stage) has no body, so new Application()does not compile (“Application is abstract; cannot be instantiated”). You write a subclass that supplies start, and the JavaFX launcher creates the object for you by calling your class's public no-argument constructor.

Three pieces turn a class into a JavaFX program:

  • extends Application makes your class a JavaFX application.
  • start(Stage primaryStage) is the abstract method you must override. JavaFX calls it once, handing it the primary stage it created, and all the code that builds your window goes inside it. The template declares throws Exception because Application declares it that way. If startdoes throw, JavaFX prints “Exception in Application start method” with the stack trace, and the program ends.
  • main calls launch(args), a static method Application provides. It starts the JavaFX runtime, creates an instance of your class and calls its start method. It does not return until the application has finished.
JavafxSample.java (the template)
read along
1public class JavafxSample extends Application {
2 @Override
3 public void start(Stage primaryStage) throws Exception {
4 /*
5 Code for JavaFX application.
6 (Stage, scene, scene graph)
7 */
8 }
9 public static void main(String args[]){
10 launch(args);
11 }
12}
Output
This is a template: it compiles once the imports
javafx.application.Application and javafx.stage.Stage are added.
Run as it stands, it shows nothing. start() never calls show(), so no
window appears, and with no window to close, the program waits until
you press Ctrl+C.

Inside start, a typical JavaFX application follows three steps:

  1. 1Prepare a scene graph with the required nodes.
  2. 2Prepare a Scene with the required dimensions, and add the scene graph to it by passing in its root node.
  3. 3Prepare a Stage, add the scene to it, and display the contents of the stage.
Key idea

Every start method does the same three things in the same order: build the tree of nodes, wrap it in a Scene, then put the Scene on the Stage and show it.

Step 1: preparing the scene graph#

A scene graph starts with its root node, since every other node hangs below it. There are three kinds of root to choose from: a Group, a Region (in practice one of its subclasses, a layout pane such as StackPane or GridPane) or a WebView. The controls and layouts lesson goes through the Region family. The examples in this lesson use a Group.

A Group (package javafx.scene) is a node whose only job is to hold other nodes. Creating one is a plain constructor call, Group root = new Group();, and an empty group shows nothing. To put nodes in it, ask for its list of children: getChildren() returns an ObservableList (package javafx.collections) holding the group's nodes, and whatever you add to that list becomes a child of the group.

Adding a node to a Group
read along
1//Retrieving the observable list object
2ObservableList list = root.getChildren();
3
4//Setting the text object as a node
5list.add(NodeObject);
Output
NodeObject stands for any node you have already created: a Text, a
Rectangle, a Button. After add(), it is a child of root, and it appears
once the scene is shown.

ObservableList is an interface that extends java.util.List, so it has the methods you would expect: add, addAll, remove, get, size. Observable means the group is notified the instant the list changes. That is why adding a node to the list is enough to put it in the window, with no refresh call, and why removing it later makes it disappear.

Two shorter forms reach the same result. The first passes the node to the constructor: Group root = new Group(NodeObject);. That constructor accepts any number of nodes, so new Group(title, photo, button) works too. And you can skip the variable: root.getChildren().add(NodeObject);.

Step 2: preparing the Scene#

A JavaFX scene is an object of the class Scene in the package javafx.scene. Passing the root to the constructor is mandatory; the size is optional.

Creating a Scene
read along
1// size worked out from the content
2Scene scene = new Scene(root);
3
4// 600 pixels wide, 300 pixels tall
5Scene scene = new Scene(root, 600, 300);
Output
Two alternatives: a program uses one or the other, since both
declare a variable named scene.

With no size, the scene makes itself just big enough for the root's content, which for an empty group means a tiny window. With a size, the scene is exactly that big, whatever the content.

Width first, then height
The constructor is Scene(Parent root, double width, double height): width first, then height. new Scene(root, 600, 300)is 600 pixels wide and 300 tall, a wide, short window. Some tutorials call the two numbers “height and width”; the window itself shows which is right. The parameters are double; the int values 600 and 300 are widened to double automatically, as in operators and casting.

A scene also has a fill, the colour behind everything in it, white unless you change it. scene.setFill(Color.BROWN) paints it brown. Color lives in javafx.scene.paint. It has a constant for every named web colour (Color.BROWN is #A52A2A, Color.NAVY is #000080), and it builds any other colour with Color.rgb(165, 42, 42) or Color.web("#A52A2A"). One more constructor takes the fill directly: new Scene(root, 600, 300, Color.BROWN).

Step 3: preparing the Stage#

The stage is the container of a JavaFX application: it provides the window. It is an object of the class Stage in the package javafx.stage, and JavaFX passes one to start() as its parameter. With that object you set the window's title with setTitle(), attach the scene with setScene(), and display the stage with show().

Preparing the Stage
read along
1//Setting the title to Stage.
2primaryStage.setTitle("Sample application");
3
4//Setting the scene to Stage
5primaryStage.setScene(scene);
6
7//Displaying the stage
8primaryStage.show();
Output
A window titled "Sample application" opens, showing the scene.

Two things about show(). Nothing is on screen before it runs; until then the stage is an object in memory. And it does not wait: show() returns at once, start reaches its closing brace, and the window stays open because the JavaFX runtime is still running the event loop on its own thread. A loop added at the end of startto “keep the window open” does the opposite: it blocks that thread and freezes the window.

The lifecycle of a JavaFX application#

LauncherthreadJavaFXApplication Threadconstructornew YourApp()init()optionalstart(stage)requiredeventswindow is openstop()optionalruns until the last window closesor Platform.exit() is calledmain() calls launch(args), which does not return until stop() has run
launch() creates your object, calls init() on the launcher thread, then start() on the JavaFX Application Thread. The program runs until the last window closes or Platform.exit() is called, and then stop() runs.

The Application class has three lifecycle methods:

  • start(): the entry point, where the JavaFX graphics code is written. It is abstract, so you must override it.
  • stop(): empty by default. Override it to write the logic that runs when the application stops, such as saving settings or closing a database connection.
  • init(): empty by default. Override it for setup that should happen before any window exists, such as reading a configuration file. You cannot create a stage or a scene in it.

Beside them is the static method launch(). Because it is static, you call it from a static context, normally main. Whenever a JavaFX application is launched, these actions happen in this order:

  1. 1An instance of your application class is created: its constructor runs.
  2. 2init() is called.
  3. 3start() is called, with the primary stage.
  4. 4The launcher waits for the application to finish, which happens when the last window is closed or the program calls Platform.exit(), and then calls stop(). Only after that does launch return to main.

Which thread runs what#

The lifecycle is spread over three threads. main runs on the main thread, which then sits inside launch until the very end. The constructor, start, stop and every event handler run on the JavaFX Application Thread. init is the odd one out: it runs on a separate launcher thread, named JavaFX-Launcher.

That is the reason behind the rule for init. A Stage and a Scene belong to the JavaFX Application Thread, and the JavaFX documentation states plainly that an application must not construct either one in init. Try to create a Stage there and it fails with IllegalStateException: Not on FX application thread; currentThread = JavaFX-Launcher. Use init for work that does not touch the screen, and build the window in start. This program prints the name of the thread at every stage.

Lifecycle.java
read along
1import javafx.application.Application;
2import javafx.scene.Group;
3import javafx.scene.Scene;
4import javafx.stage.Stage;
5
6public class Lifecycle extends Application {
7
8 public Lifecycle() {
9 System.out.println("constructor on " + Thread.currentThread().getName());
10 }
11
12 @Override
13 public void init() {
14 System.out.println("init on " + Thread.currentThread().getName());
15 }
16
17 @Override
18 public void start(Stage stage) {
19 System.out.println("start on " + Thread.currentThread().getName());
20 stage.setTitle("Lifecycle");
21 stage.setScene(new Scene(new Group(), 300, 150));
22 stage.show();
23 }
24
25 @Override
26 public void stop() {
27 System.out.println("stop on " + Thread.currentThread().getName());
28 }
29
30 public static void main(String[] args) {
31 System.out.println("main on " + Thread.currentThread().getName());
32 launch(args);
33 System.out.println("launch returned, main ends");
34 }
35}
Output
main on main
constructor on JavaFX Application Thread
init on JavaFX-Launcher
start on JavaFX Application Thread
   (a 300 by 150 window titled "Lifecycle" is open; close it)
stop on JavaFX Application Thread
launch returned, main ends

Read the output against the steps. main prints first, on the main thread. The constructor and start share the JavaFX Application Thread, while init runs between them on the launcher thread. Nothing more happens until you close the window. Then stop runs, and only once it has finished does launch return and let main print its last line.

Terminating a JavaFX application#

By default a JavaFX application ends implicitly: when its last window is closed, the launcher calls stop() and the program finishes. You can switch this off by passing false to setImplicitExit().

setImplicitExit, in full
In full it is Platform.setImplicitExit(false), a static method of the class Platform in javafx.application. Being static means you call it through the class name. It does not have to be called from a static method, and the documentation says it may be called from any thread, so start or init is a natural place for it. With implicit exit off, closing the last window leaves the program running with no window at all until it calls Platform.exit() itself, which suits a program that hides its window and shows it again later.

To end the application explicitly, there are two methods, Platform.exit() and System.exit(int). They do not do the same thing:

Platform.exit()
System.exit(int)
JavaFX's own shutdown: closes the windows and stops the JavaFX runtime.
Stops the whole JVM on the spot.
Your stop() method runs first.
Your stop() method never runs.
Threads you started yourself keep the JVM alive until they finish.
Every thread stops, finished or not.
The usual choice for a Quit button.
For emergencies, or when the program must report an exit status: 0 for success, anything else for an error.
Ending.java (three separate fragments)
read along
1import javafx.application.Platform;
2
3// in start(): closing the last window no longer ends the program
4Platform.setImplicitExit(false);
5
6// later, in a Quit button's handler: a proper shutdown, stop() runs
7Platform.exit();
8
9// or end the JVM at once with exit status 0; stop() is skipped
10System.exit(0);
Output
Button handlers arrive in the event handling session. Until then,
read Platform.exit() as the call a Quit button should make.

Example: creating an empty window#

Here is one complete program, JavafxSample, built in seven steps. The result is a window titled “Sample Application”, 600 pixels wide and 300 tall, filled brown and otherwise empty.

  1. 1Create a class that inherits the Application class of the package javafx.application and implements its start() method: public class JavafxSample extends Application, containing an empty public void start(Stage primaryStage) throws Exception marked @Override.
  2. 2Create a Group object inside start(), by instantiating the class Group of the package javafx.scene: Group root = new Group();
  3. 3Create a Scene object, passing the group as the root, along with the size: Scene scene = new Scene(root, 600, 300); The two numbers are the width and height of the scene, in that order.
  4. 4Set the title of the stage through the primaryStage parameter: primaryStage.setTitle("Sample Application"); JavaFX passes the stage to start in your Application subclass.
  5. 5Add the scene to the stage: primaryStage.setScene(scene);
  6. 6Display the contents of the stage: primaryStage.show();
  7. 7Launch the application by calling the static method launch() from main: public static void main(String args[]){ launch(args); }. The form String args[] is an older way of writing String[] args; both mean the same.

Put together with its imports and one extra line that colours the scene, this is the whole program. The group variable is called group here rather than root; any name works.

JavafxSample.java
read along
1import javafx.application.Application;
2import javafx.scene.Group;
3import javafx.scene.Scene;
4import javafx.scene.paint.Color;
5import javafx.stage.Stage;
6
7public class JavafxSample extends Application {
8 @Override
9 public void start(Stage primaryStage) throws Exception {
10 //creating a Group object
11 Group group = new Group();
12
13 //Creating a Scene by passing the group object, width and height
14 Scene scene = new Scene(group ,600, 300);
15
16 //setting color to the scene
17 scene.setFill(Color.BROWN);
18
19 //Setting the title to Stage.
20 primaryStage.setTitle("Sample Application");
21
22 //Adding the scene to Stage
23 primaryStage.setScene(scene);
24
25 //Displaying the contents of the stage
26 primaryStage.show();
27 }
28 public static void main(String args[]){
29 launch(args);
30 }
31}
Output
A window titled "Sample Application" whose 600 by 300 content area is
filled brown (Color.BROWN, #A52A2A). Nothing is printed by the program
itself. Close the window and the program ends.
Sample Application
The output: a 600 by 300 content area, all Color.BROWN (#A52A2A). The group is empty, so the fill is all there is to see.

Compiling and running from the command line#

Since Java 11, JavaFX is not part of the JDK, so javac and java cannot find it on their own. You download the JavaFX SDK from openjfx.io, choosing version 27 and your own operating system and processor, and unzip it, for example to C:\Program Files\javafx-sdk-27. Its lib folder holds JavaFX's modules as .jar files (javafx.base.jar, javafx.graphics.jar, javafx.controls.jar and the rest), and every command below points at that folder.

Pair JavaFX 27 with JDK 25 or newer
Check with java -version and javac -version; both should report 25 or higher. Each JavaFX release states a minimum JDK (JavaFX 25, for instance, needs JDK 23 or newer), and an older JDK refuses to load newer JavaFX classes, with an error that mentions a class file version. On an Apple Silicon Mac, download the aarch64 SDK rather than the x64 one.

PATH_TO_FX and the Command Prompt commands#

The first pair of commands uses %PATH_TO_FX%. PATH_TO_FX is an environment variable, a named value the terminal keeps, which you create so the long path to the lib folder is typed only once. In the Windows Command Prompt, set creates it and %PATH_TO_FX% stands for its value:

Command Prompt
read along
1set PATH_TO_FX="C:\Program Files\javafx-sdk-27\lib"
2javac --module-path %PATH_TO_FX% --add-modules javafx.controls JavafxSample.java
3java --module-path %PATH_TO_FX% --add-modules javafx.controls JavafxSample
Output
javac prints nothing and leaves JavafxSample.class beside the .java file.
java opens the brown window. On JDK 24 or newer it also prints a few
WARNING lines about a restricted method; the VS Code commands below add
the flag that removes them.

The quotes in the set line become part of the value. When %PATH_TO_FX%is expanded, the path arrives with its quotes, so the space in “Program Files” does not split it in two. A variable made with set lasts only as long as that Command Prompt window; open a new window and you must set it again.

The VS Code commands#

In the terminal inside VS Code the commands look different. On Windows that terminal runs PowerShell by default, and PowerShell does not understand %PATH_TO_FX%, so these write the path out in full:

VS Code terminal
read along
1javac --module-path "C:\Program Files\javafx-sdk-27\lib" --add-modules javafx.controls -d out JavafxSample.java
2java --enable-native-access=javafx.graphics --module-path "C:\Program Files\javafx-sdk-27\lib" --add-modules javafx.controls -cp out JavafxSample
Output
javac creates a folder named out and puts JavafxSample.class in it.
java loads the class from out and opens the brown window, with no warnings.

Every flag in those two commands, and what it is for:

FlagUsed withWhat it does
--module-pathjavac and javaWhere to look for modules: here, JavaFX's lib folder. On its own it only makes them findable.
--add-modules javafx.controlsjavac and javaWhich modules to load. Your class is not part of any module, so nothing asks for JavaFX until you name it here. javafx.controls brings javafx.graphics and javafx.base with it. Add more, separated by commas, when you use them: javafx.controls,javafx.media.
-d outjavacWrites the compiled .class files into the folder out, creating it if needed, instead of beside the source file.
-cp outjavaThe classpath: where to find your own classes. It must name the folder -d wrote to, or java cannot find JavafxSample.
--enable-native-access=javafx.graphicsjavaAllows JavaFX's graphics module to load its native libraries, the compiled code that talks to the GPU and the operating system. From JDK 24 the JVM warns about any library that does this without permission. If you use media or WebView, list those modules too: --enable-native-access=javafx.graphics,javafx.media.
Key idea

Both commands need to know where JavaFX is (--module-path) and which parts of it to load (--add-modules). Give the flags to javac but not to java, and the program compiles and then refuses to run.

On macOS and Linux#

The same commands work with three changes: export instead of set, $PATH_TO_FX instead of %PATH_TO_FX%, and forward slashes in the path.

Terminal (macOS, Linux)
read along
1export PATH_TO_FX=$HOME/javafx-sdk-27/lib
2javac --module-path $PATH_TO_FX --add-modules javafx.controls -d out JavafxSample.java
3java --enable-native-access=javafx.graphics --module-path $PATH_TO_FX --add-modules javafx.controls -cp out JavafxSample
Output
Assumes the SDK was unzipped into your home folder; change the path if not.
The window opens just as it does on Windows.

What you see when a flag is missing#

Each of these catches someone the first time. The message tells you which command, and which flag, to look at.

What you seeWhyFix
error: package javafx.application does not exist, from javac, followed by an error for each JavaFX class you usejavac cannot see JavaFX: the module flags are missing from the javac command.Add --module-path and --add-modules javafx.controls to javac.
Error: Could not find or load main class JavafxSample with Caused by: java.lang.NoClassDefFoundError: javafx/application/Application, from javaThe class compiled with the flags but was run without them, so java cannot find JavaFX at all.Add the same two flags to the java command. Compiling with them is not enough.
Error: JavaFX runtime components are missing, and are required to run this application, from javaJavaFX's jar files are on the class path instead of the module path, as happens when an IDE or a build file adds them as ordinary libraries.Load JavaFX with --module-path and --add-modules. The guide to this error covers Maven, Gradle and each IDE.
WARNING: A restricted method in java.lang.System has been called, plus three more WARNING lines; the window still opensJDK 24 and newer warn whenever a library loads native code without permission.Add --enable-native-access=javafx.graphics to java, as one of the warning lines itself suggests.
Error occurred during initialization of boot layer and java.lang.module.FindException: Module javafx.controls not found (from javac: error: module not found: javafx.controls)The module path points at the SDK folder (javafx-sdk-27) instead of its lib folder, or the path is mistyped, or PATH_TO_FX was set in a different window.Point --module-path at ...\javafx-sdk-27\lib, and check the variable with echo %PATH_TO_FX%.
Error: Could not find or load main class JavafxSample with Caused by: java.lang.ClassNotFoundException: JavafxSampleCompiled with -d out but run without -cp out, so java looked in the current folder.Add -cp out, naming the same folder as -d.

In an IDE#

An IDE needs the same two pieces of information, entered once in its settings instead of typed every time.

  • VS Code. For compiling, add the jars from the SDK's lib folder under Referenced Libraries in the Java Projects view, which clears the red underlines on the imports. For running, put the flags in .vscode/launch.json as vmArgs, as below, and run with F5.
  • IntelliJ IDEA. Add the lib folder as a library under File, Project Structure, Libraries. Then open Run, Edit Configurations, choose Modify options, Add VM options, and paste the flags: --module-path "C:\Program Files\javafx-sdk-27\lib" --add-modules javafx.controls --enable-native-access=javafx.graphics. Alternatively, start from IntelliJ's New Project wizard and pick its JavaFX generator. It creates a Maven project that downloads JavaFX by itself, and you run it with the javafx:run goal in the Maven tool window, or mvn javafx:run in a terminal, with no module path to manage.
.vscode/launch.json
read along
1{
2 "version": "0.2.0",
3 "configurations": [
4 {
5 "type": "java",
6 "name": "JavafxSample",
7 "request": "launch",
8 "mainClass": "JavafxSample",
9 "vmArgs": "--module-path \"C:/Program Files/javafx-sdk-27/lib\" --add-modules javafx.controls --enable-native-access=javafx.graphics"
10 }
11 ]
12}
Output
Forward slashes work in Java paths on Windows and avoid escaping backslashes
in JSON. The \" marks around the path keep its space from splitting it.

Example: displaying text#

The second example embeds text in the scene. Save it in a file named DisplayingText.java. It follows the same three steps as the empty window, with one node in the group: a Text.

DisplayingText.java
read along
1import javafx.application.Application;
2import javafx.collections.ObservableList;
3import javafx.scene.Group;
4import javafx.scene.Scene;
5import javafx.stage.Stage;
6import javafx.scene.text.Font;
7import javafx.scene.text.Text;
8
9public class DisplayingText extends Application {
10 @Override
11 public void start(Stage stage) {
12 //Creating a Text object
13 Text text = new Text();
14
15 //Setting font to the text
16 text.setFont(new Font(45));
17
18 //setting the position of the text
19 text.setX(50);
20 text.setY(150);
21
22 //Setting the text to be added.
23 text.setText("Welcome to Ooty");
24
25 //Creating a Group object
26 Group root = new Group();
27
28 //Retrieving the observable list object
29 ObservableList list = root.getChildren();
30
31 //Setting the text object as a node to the group object
32 list.add(text);
33
34 //Creating a scene object
35 Scene scene = new Scene(root, 600, 300);
36
37 //Setting title to the Stage
38 stage.setTitle("Sample Application");
39
40 //Adding scene to the stage
41 stage.setScene(scene);
42
43 //Displaying the contents of the stage
44 stage.show();
45 }
46 public static void main(String args[]){
47 launch(args);
48 }
49}
Output
javac compiles it and prints two notes (explained below):
Note: DisplayingText.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

java opens a 600 by 300 window titled "Sample Application" showing
"Welcome to Ooty" in large letters that start 50 pixels from the left
and stand on a line 150 pixels from the top.

Line by line: Text (package javafx.scene.text) is a node that draws a string. It is a shape, a cousin of Rectangle and Circle, so it is placed by coordinates. new Font(45) asks for the default system font at size 45, headline letters in a 300-pixel-tall window. setText supplies the words. The rest is the familiar pattern: the text goes into the group through its list of children, the group into a 600 by 300 scene, and the scene onto the stage.

Sample Application
Welcome to Ooty
DisplayingText with the two added lines. The words start 50 pixels from the left and stand on a line 150 pixels down, half way down the window. The dashed line and the dot at (50, 150) are drawn here to show the position; they are not in the real window.

Why y is the baseline#

Letters stand on an invisible line called the baseline. Most of each letter sits above it, and the tails of letters such as y, g and p hang below. A Text node's x is where its first letter starts, and its y is the height of its baseline, measured down from the top of the scene. So setY(150) stands the words on a line half way down the 300-pixel window, as in the sketch above.

Now leave out the setX and setYlines and run it again. Without them, both values are 0, so the baseline lies along the window's top edge. Every letter is drawn above it, outside the window, and a group does nothing to bring it back into view. All you see is the tail of the y.

Sample Application
Welcome to Ooty
Without setX and setY. With x and y both 0, the baseline is the top edge of the window, so the words are drawn above it. Only the tail of the y reaches down into view.
Key idea

A Text node's y is its baseline, the line its letters stand on, not its top edge.

Text or Label?#

JavaFX has a second node for words, Label, and the two suit different jobs. Text is a shape: you place it at exact coordinates, fill and outline it like any shape, and it has no padding, background or border of its own. Label (package javafx.scene.control) is a control: a layout pane decides where it goes, CSS can give it padding, a background and a border, it can show an icon beside its words, and it shortens its text with “...” when space runs out. Use Text for words drawn at a chosen point in a Group, as here, and Label for the captions in a form, as the registration form does.

The note javac prints#

Compiling DisplayingText prints two lines that begin with Note:. They come from ObservableList list = root.getChildren();. getChildren() really returns an ObservableList<Node>, a list of nodes, but the variable is declared as a plain ObservableList without the <Node>, which is called a raw type. With a raw type the compiler cannot check that only nodes go into the list, so it flags list.add(text) as unchecked. A note does not stop the build: the class file is written and the program runs. To silence it, give the list its type, ObservableList<Node> list = root.getChildren(); with import javafx.scene.Node;, or skip the variable: root.getChildren().add(text);.

Compile and run it the same way as the first example:

Command Prompt
read along
1javac --module-path %PATH_TO_FX% --add-modules javafx.controls DisplayingText.java
2java --module-path %PATH_TO_FX% --add-modules javafx.controls DisplayingText
Output
The two Note lines from javac, then the window. In VS Code's terminal use
the full-path form with -d out, -cp out and --enable-native-access=javafx.graphics,
exactly as for JavafxSample.

Try it

Answer each one in your head or on paper first, then open the answer.

1
Change JavafxSample so the window is 400 pixels wide, 250 tall and dark blue (Color.NAVY). Which lines change? Write them.
2
Someone tells you that new Scene(root, 600, 300) makes a scene 600 pixels tall. Is 600 the height? How could you check without reading the documentation?
3
Your DisplayingText compiles, but running it prints Error: Could not find or load main class DisplayingText and Caused by: java.lang.NoClassDefFoundError: javafx/application/Application. A friend's fails earlier, at javac, with error: package javafx.application does not exist. What is missing in each case? Write the corrected Command Prompt commands.
4
Make the DisplayingText window a utility window. Write the line, say where it goes and which import it needs. What happens if you put it after stage.show()?
5
Centre “Welcome to Ooty” in the window without working out any coordinates, by replacing the Group with a StackPane. Write the changed lines. What happens to setX and setY, and what happens when you resize the window?
6
Add a stop() method to DisplayingText that prints “Goodbye from Ooty”. When does the line appear? Would it appear if a Quit button called System.exit(0)? And if it called Platform.exit()?

Practice

Work through these on your own. Each one builds on this page.

1
Add a second Text to DisplayingText: “Queen of Hill Stations” at size 30, placed at (50, 220). Sketch on paper where you expect it before you run the program, then compare.
2
Write Lifecycle.java and run it. Then add Platform.setImplicitExit(false); as the first line of start, run it again and close the window. What does the terminal show now, and how do you end the program? Explain why stop did not run, and say what would have to call Platform.exit().
3
From a terminal, compile and run JavafxSample five times, each time with one thing wrong: no --module-path; no --add-modules; no -cp out; no --enable-native-access; and the module path pointing at the SDK folder instead of its lib folder. Copy the first two lines of each message into a table next to its cause. Keep the table for the next time something fails.
4
Rewrite DisplayingText using a Label instead of a Text, inside a StackPane root. Which lines and imports change? Then add label.setStyle("-fx-background-color: lightyellow; -fx-padding: 10;") and describe what the Label can show that the Text could not.

Short guides on this topic

Quick answers to what people search for about it, with code to run.

Spotted a mistake, or a part that did not make sense? Report it by email